From c2406d9f11d2ce8889818dd0e5147865a084c64a Mon Sep 17 00:00:00 2001 From: Andy Taylor Date: Thu, 19 Mar 2026 19:14:17 +0000 Subject: [PATCH] Remove wxWidgets, port to standalone C++17 with cross-platform support Replace the wxWidgets dependency with standard C++17 throughout the entire codebase, producing a clean, portable, thoroughly reviewed CLI daemon that builds on Linux, Windows, and macOS without any GUI toolkit. == Removed == - GUI application and config editor (DStarRepeaterFrame, DStarRepeaterConfig/) - GUI widget library (GUICommon/) - Windows USB drivers (WindowsUSB/) - Visual Studio solutions and project files (.sln, .vcxproj) - NSIS installer scripts, platform-specific makefiles - wxWidgets-specific files (LogEvent, LogRedirect, GMSKModemWinUSB) - Dead code: unused GUI constants, vestigial command methods, unused DTMF/queue constants in non-TRX thread variants == Core conversion (all source files) == - wxString -> std::string, wxArrayString -> std::vector - wxFloat32 -> float, wxUint8/16/32 -> uint8_t/16_t/32_t - wxThread -> std::thread, wxMutex -> std::mutex - wxStopWatch -> std::chrono::steady_clock - wxRegEx -> std::regex, wxDynLib -> dlopen/dlsym - wxFFile -> FILE*, wxTextFile -> std::ifstream - WX_DECLARE_STRING_HASH_MAP -> std::unordered_map - volatile -> std::atomic for ring buffer and cross-thread flags - NULL -> nullptr throughout == INI config format (MMDVMHost compatible) == - [Section]/Key=Value INI format replaces flat key=value - Config file path is the single required command-line argument: dstarrepeaterd - [Log] section: FilePath, FileLevel, DisplayLevel, MQTTLevel (levels 0-6 matching MMDVMHost convention) - [Paths] section: Data and Audio directories from config - [Whitelist]/[Blacklist]/[Greylist] file paths from config - All compile-time path defines (CONF_DIR, LOG_DIR) removed - Example config: Data/dstarrepeater.ini.example == Build system == - Plain g++ -std=c++17, no wx-config dependency - Defaults: release build, MQTT on, GPIO auto-detected - GPIO auto-detection checks uname -s (Linux only) and uname -m (arm/aarch64) to avoid false match on macOS Apple Silicon - Zero warnings with -Wall on GCC 13 - Verified on Ubuntu 24.04, Debian Trixie, and Alpine 3.21 == Install layout == - Binary: /usr/bin/dstarrepeaterd - AMBE voice data: /usr/share/dstarrepeater/ - Config directory: /etc/dstarrepeater/ (created on install) - Example config: /etc/dstarrepeater/dstarrepeater.ini.example == Cross-platform support == - EndianCompat.h: byte-swap functions for Linux, macOS, and Windows - Serial ports: Win32 CreateFile/ReadFile/WriteFile alongside POSIX - Sockets: Winsock2 with RAII WSAStartup alongside POSIX - Sound card: PortAudio for Windows/macOS, ALSA for Linux - All platform code is additive (#ifdef _WIN32), no Linux code changed == Standalone logger == - Thread-safe file logger with std::mutex and daily UTC rotation - Level-based filtering for file, display (stdout), and MQTT sinks - LogLevel enum matches MMDVMHost numbering (1=Debug through 6=Fatal) - Thread-safe gmtime via gmtime_r/gmtime_s - Atomic singleton for safe cross-thread access == Display-Driver MQTT compatibility == - All JSON messages match Display-Driver's expected format exactly - D-Star start/end/lost events with callsigns and source (rf/net) - BER updates during active RF - DVAP RSSI signal strength publishing - Slow-data text forwarding - MMDVM idle mode transitions - JSON escaping on all callsign/text fields from over-the-air data - JSON schema: schema.json with validated $defs structure == Security and safety fixes == - All cross-thread bool flags: std::atomic - MQTTConnection m_connected and DVAP m_signal/m_squelchOpen: atomic - RingBuffer: std::atomic for ARM correctness - All sprintf -> snprintf to prevent buffer overflows - JSON escaping for all MQTT output from untrusted sources - DVTOOLFileWriter filename sanitization (path traversal prevention) - Thread-safe localtime_r/localtime_s in DVTOOLFileWriter - GatewayProtocolHandler: bounded string from network buffer - Integer underflow guards in protocol handler readData() - Buffer bounds checks in writePoll()/writeRegister() - MMDVM printDebug() minimum length guards - PID file: /var/run with O_NOFOLLOW, PID written for systemd - Exception-safe createThread() with full cleanup on failure - Deprecated gethostbyname() replaced with getaddrinfo() - CRLF stripping in callsign list loader - Infinite loop exits in DVAP resync() and serial getResponse() - Null guards in SoundCardReaderWriter::close() - RingBuffer::hasSpace() off-by-one fixed == Memory safety == - Delete protocol handler, modem, controller on open()/start() failure - Thread destructors clean up all owned objects with null-after-delete - Callsign lists properly deleted in TX/RX/TXRX thread no-op setters - BeaconUnit buffer allocation matched to actual write size (DV_FRAME) - Null dereference fix in endOfNetworkData() for network-first streams == Functional fixes == - Restored DTMF command execution (system() via checkControl()) - Fixed getControl() parameter order (commands 3-6 were swapped) - Fixed getStatus() argument order in idle branch (BER was at wrong position, causing MQTT to report garbage BER when idle) - Added missing GMSK interface type config parsing - Fixed IcomController uninitialized buffer and sync check order - Fixed SplitController format string vulnerability - Fixed DVAPController dumpPackets() dead loop - Inline const arrays in DStarDefines.h (ODR compliance) == Documentation == - Comprehensive comments added to all source files - README.md: quick start, dependency tables, usage guide - CONFIGURATION.md: complete INI format reference - MQTT.md: updated for INI config, Display-Driver integration - BUILD.md: per-platform instructions (Linux, Windows, macOS) - CHANGELOG.md: version history, newest first - schema.json: JSON schema for all MQTT messages Version bumped to 20260319. --- .gitignore | 1 + BUILD.md | 133 + BUILD.txt | 70 - CHANGELOG.md | 1757 +++++++++++++ CHANGES.txt | 2098 --------------- CONFIGURATION.md | 502 ++-- Common/AMBEFEC.cpp | 7 +- Common/AMBEFEC.h | 11 + Common/AnnouncementCallback.h | 2 + Common/AnnouncementUnit.cpp | 74 +- Common/AnnouncementUnit.h | 29 +- Common/ArduinoController.cpp | 2 +- Common/ArduinoController.h | 5 +- Common/AudioCallback.h | 9 +- Common/BeaconCallback.h | 2 + Common/BeaconUnit.cpp | 289 +- Common/BeaconUnit.h | 75 +- Common/CCITTChecksum.cpp | 11 +- Common/CCITTChecksum.h | 16 +- Common/CCITTChecksumReverse.cpp | 11 +- Common/CCITTChecksumReverse.h | 15 +- Common/CallsignList.cpp | 49 +- Common/CallsignList.h | 20 +- Common/Common.vcxproj | 266 -- Common/Common.vcxproj.filters | 332 --- Common/DStarDefines.h | 195 +- Common/DStarGMSKDemodulator.cpp | 10 +- Common/DStarGMSKDemodulator.h | 36 +- Common/DStarGMSKModulator.cpp | 10 +- Common/DStarGMSKModulator.h | 11 +- Common/DStarRepeaterConfig.cpp | 2322 +++++------------ Common/DStarRepeaterConfig.h | 306 ++- Common/DStarScrambler.cpp | 18 +- Common/DStarScrambler.h | 14 +- Common/DVAPController.cpp | 272 +- Common/DVAPController.h | 66 +- Common/DVMegaController.cpp | 392 ++- Common/DVMegaController.h | 57 +- Common/DVRPTRV1Controller.cpp | 345 ++- Common/DVRPTRV1Controller.h | 33 +- Common/DVRPTRV2Controller.cpp | 321 +-- Common/DVRPTRV2Controller.h | 52 +- Common/DVRPTRV3Controller.cpp | 316 +-- Common/DVRPTRV3Controller.h | 31 +- Common/DVTOOLFileReader.cpp | 71 +- Common/DVTOOLFileReader.h | 49 +- Common/DVTOOLFileWriter.cpp | 269 +- Common/DVTOOLFileWriter.h | 43 +- Common/DummyController.cpp | 2 - Common/EndianCompat.h | 35 + Common/ExternalController.cpp | 44 +- Common/ExternalController.h | 67 +- Common/FIRFilter.cpp | 63 +- Common/FIRFilter.h | 27 +- Common/GMSKController.cpp | 124 +- Common/GMSKController.h | 30 +- Common/GMSKModem.h | 17 +- Common/GMSKModemLibUsb.cpp | 190 +- Common/GMSKModemLibUsb.h | 60 +- Common/GMSKModemWinUSB.cpp | 381 --- Common/GMSKModemWinUSB.h | 60 - Common/GPIOController.cpp | 5 +- Common/GPIOController.h | 3 - Common/GatewayProtocolHandler.cpp | 42 +- Common/GatewayProtocolHandler.h | 34 +- Common/Golay.h | 10 + Common/HardwareController.h | 12 + Common/HeaderData.cpp | 103 +- Common/HeaderData.h | 72 +- Common/IcomController.cpp | 155 +- Common/IcomController.h | 51 +- Common/K8055Controller.cpp | 274 +- Common/K8055Controller.h | 34 +- Common/LogEvent.cpp | 45 - Common/LogEvent.h | 40 - Common/Logger.cpp | 193 +- Common/Logger.h | 92 +- Common/MMDVMController.cpp | 380 ++- Common/MMDVMController.h | 59 +- Common/MQTTConnection.cpp | 18 +- Common/MQTTConnection.h | 23 +- Common/MQTTPublisher.h | 86 +- Common/Makefile | 13 +- Common/Modem.cpp | 15 +- Common/Modem.h | 65 +- Common/OutputQueue.cpp | 29 +- Common/OutputQueue.h | 25 +- Common/RepeaterProtocolHandler.cpp | 129 +- Common/RepeaterProtocolHandler.h | 52 +- Common/RingBuffer.h | 41 +- Common/SerialDataController.cpp | 466 ++-- Common/SerialDataController.h | 30 +- Common/SerialLineController.cpp | 461 ++-- Common/SerialLineController.h | 38 +- Common/SerialPortSelector.cpp | 122 +- Common/SerialPortSelector.h | 4 +- Common/SlowDataDecoder.cpp | 21 +- Common/SlowDataDecoder.h | 33 +- Common/SlowDataEncoder.cpp | 128 +- Common/SlowDataEncoder.h | 33 +- Common/SoundCardController.cpp | 153 +- Common/SoundCardController.h | 62 +- Common/SoundCardReaderWriter.cpp | 723 +++-- Common/SoundCardReaderWriter.h | 130 +- Common/SplitController.cpp | 257 +- Common/SplitController.h | 94 +- .../StdCompat.h | 39 +- Common/TCPReaderWriter.cpp | 159 +- Common/TCPReaderWriter.h | 37 +- Common/Timer.cpp | 14 +- Common/Timer.h | 19 +- Common/UDPReaderWriter.cpp | 142 +- Common/UDPReaderWriter.h | 32 +- Common/UDRCController.cpp | 27 +- Common/URIUSBController.cpp | 267 +- Common/URIUSBController.h | 35 +- Common/Utils.cpp | 31 +- Common/Utils.h | 12 +- Common/Version.h | 14 +- DStarRepeater.sln | 61 - DStarRepeater/DStarRepeater.vcxproj | 204 -- DStarRepeater/DStarRepeater.vcxproj.filters | 74 - DStarRepeater/DStarRepeaterApp.cpp | 1081 ++++---- DStarRepeater/DStarRepeaterApp.h | 70 +- DStarRepeater/DStarRepeaterDefs.h | 23 +- DStarRepeater/DStarRepeaterFrame.cpp | 586 ----- DStarRepeater/DStarRepeaterFrame.h | 83 - DStarRepeater/DStarRepeaterLogRedirect.cpp | 56 - DStarRepeater/DStarRepeaterLogRedirect.h | 35 - DStarRepeater/DStarRepeaterRXThread.cpp | 171 +- DStarRepeater/DStarRepeaterRXThread.h | 37 +- DStarRepeater/DStarRepeaterStatusData.cpp | 95 +- DStarRepeater/DStarRepeaterStatusData.h | 75 +- DStarRepeater/DStarRepeaterTRXThread.cpp | 747 ++++-- DStarRepeater/DStarRepeaterTRXThread.h | 245 +- DStarRepeater/DStarRepeaterTXRXThread.cpp | 231 +- DStarRepeater/DStarRepeaterTXRXThread.h | 48 +- DStarRepeater/DStarRepeaterTXThread.cpp | 179 +- DStarRepeater/DStarRepeaterTXThread.h | 43 +- DStarRepeater/DStarRepeaterThread.cpp | 3 +- DStarRepeater/DStarRepeaterThread.h | 60 +- DStarRepeater/Makefile | 8 +- DStarRepeater/MakefileGUI | 23 - DStarRepeater32.nsi | 108 - DStarRepeater64.nsi | 108 - .../DStarRepeaterConfig.vcxproj | 230 -- .../DStarRepeaterConfig.vcxproj.filters | 152 -- .../DStarRepeaterConfigActiveHangSet.cpp | 59 - .../DStarRepeaterConfigAnnouncementSet.cpp | 130 - .../DStarRepeaterConfigAnnouncementSet.h | 51 - .../DStarRepeaterConfigApp.cpp | 93 - DStarRepeaterConfig/DStarRepeaterConfigApp.h | 47 - .../DStarRepeaterConfigBeaconSet.cpp | 123 - .../DStarRepeaterConfigBeaconSet.h | 47 - .../DStarRepeaterConfigCallsignSet.cpp | 334 --- .../DStarRepeaterConfigCallsignSet.h | 60 - .../DStarRepeaterConfigControl1Set.cpp | 234 -- .../DStarRepeaterConfigControl1Set.h | 74 - .../DStarRepeaterConfigControl2Set.cpp | 180 -- .../DStarRepeaterConfigControl2Set.h | 71 - .../DStarRepeaterConfigControllerSet.cpp | 164 -- .../DStarRepeaterConfigControllerSet.h | 47 - .../DStarRepeaterConfigDVAPSet.cpp | 170 -- .../DStarRepeaterConfigDVAPSet.h | 48 - .../DStarRepeaterConfigDVMegaSet.cpp | 311 --- .../DStarRepeaterConfigDVMegaSet.h | 57 - .../DStarRepeaterConfigDVRPTR1Set.cpp | 174 -- .../DStarRepeaterConfigDVRPTR1Set.h | 50 - .../DStarRepeaterConfigDVRPTR2Set.cpp | 232 -- .../DStarRepeaterConfigDVRPTR2Set.h | 58 - .../DStarRepeaterConfigDVRPTR3Set.cpp | 232 -- .../DStarRepeaterConfigDVRPTR3Set.h | 58 - DStarRepeaterConfig/DStarRepeaterConfigDefs.h | 28 - .../DStarRepeaterConfigFrame.cpp | 294 --- .../DStarRepeaterConfigFrame.h | 64 - .../DStarRepeaterConfigGMSKSet.cpp | 134 - .../DStarRepeaterConfigGMSKSet.h | 44 - .../DStarRepeaterConfigIcomSet.cpp | 82 - .../DStarRepeaterConfigIcomSet.h | 40 - .../DStarRepeaterConfigMMDVMSet.cpp | 187 -- .../DStarRepeaterConfigMMDVMSet.h | 52 - .../DStarRepeaterConfigModemSet.cpp | 285 -- .../DStarRepeaterConfigModemSet.h | 47 - .../DStarRepeaterConfigNetworkSet.cpp | 144 - .../DStarRepeaterConfigNetworkSet.h | 49 - .../DStarRepeaterConfigSoundCardSet.cpp | 206 -- .../DStarRepeaterConfigSoundCardSet.h | 54 - .../DStarRepeaterConfigSplitSet.cpp | 162 -- .../DStarRepeaterConfigSplitSet.h | 54 - .../DStarRepeaterConfigTimesSet.cpp | 71 - .../DStarRepeaterConfigTimesSet.h | 40 - DStarRepeaterConfig/Makefile | 27 - Data/dstarrepeater.ini.example | 221 ++ GUICommon/AddressTextCtrl.cpp | 29 - GUICommon/AddressTextCtrl.h | 37 - GUICommon/CallsignTextCtrl.cpp | 29 - GUICommon/CallsignTextCtrl.h | 37 - GUICommon/GUICommon.vcxproj | 151 -- GUICommon/GUICommon.vcxproj.filters | 47 - GUICommon/Makefile | 17 - GUICommon/MessageTextCtrl.cpp | 29 - GUICommon/MessageTextCtrl.h | 37 - GUICommon/PortTextCtrl.cpp | 29 - GUICommon/PortTextCtrl.h | 37 - GUICommon/RestrictedTextCtrl.cpp | 40 - GUICommon/RestrictedTextCtrl.h | 32 - MQTT.md | 272 +- Makefile | 53 +- MakefileGUI | 52 - MakefileGUIPi | 41 - MakefilePi | 41 - README.md | 213 +- WindowsUSB/dvrptr_cdc.inf | 76 - WindowsUSB/gmsk.cat | Bin 4061 -> 0 bytes WindowsUSB/gmsk.inf | 181 -- WindowsUSB/xDVRPTR-32-64-2.inf | 95 - debian/dstarrepeaterd.dstarrepeaterd@.service | 4 +- linux/dstarrepeater.example | 122 - schema.json | 259 ++ 219 files changed, 10556 insertions(+), 19437 deletions(-) create mode 100644 BUILD.md delete mode 100644 BUILD.txt create mode 100644 CHANGELOG.md delete mode 100644 CHANGES.txt delete mode 100644 Common/Common.vcxproj delete mode 100644 Common/Common.vcxproj.filters create mode 100644 Common/EndianCompat.h delete mode 100644 Common/GMSKModemWinUSB.cpp delete mode 100644 Common/GMSKModemWinUSB.h delete mode 100644 Common/LogEvent.cpp delete mode 100644 Common/LogEvent.h rename DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.h => Common/StdCompat.h (53%) delete mode 100644 DStarRepeater.sln delete mode 100644 DStarRepeater/DStarRepeater.vcxproj delete mode 100644 DStarRepeater/DStarRepeater.vcxproj.filters delete mode 100644 DStarRepeater/DStarRepeaterFrame.cpp delete mode 100644 DStarRepeater/DStarRepeaterFrame.h delete mode 100644 DStarRepeater/DStarRepeaterLogRedirect.cpp delete mode 100644 DStarRepeater/DStarRepeaterLogRedirect.h delete mode 100644 DStarRepeater/MakefileGUI delete mode 100644 DStarRepeater32.nsi delete mode 100644 DStarRepeater64.nsi delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfig.vcxproj delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfig.vcxproj.filters delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigApp.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigApp.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigControl1Set.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigControl1Set.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigControl2Set.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigControl2Set.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigControllerSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigControllerSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigDefs.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigFrame.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigFrame.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigIcomSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigIcomSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigModemSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigModemSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigSplitSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigSplitSet.h delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigTimesSet.cpp delete mode 100644 DStarRepeaterConfig/DStarRepeaterConfigTimesSet.h delete mode 100644 DStarRepeaterConfig/Makefile create mode 100644 Data/dstarrepeater.ini.example delete mode 100644 GUICommon/AddressTextCtrl.cpp delete mode 100644 GUICommon/AddressTextCtrl.h delete mode 100644 GUICommon/CallsignTextCtrl.cpp delete mode 100644 GUICommon/CallsignTextCtrl.h delete mode 100644 GUICommon/GUICommon.vcxproj delete mode 100644 GUICommon/GUICommon.vcxproj.filters delete mode 100644 GUICommon/Makefile delete mode 100644 GUICommon/MessageTextCtrl.cpp delete mode 100644 GUICommon/MessageTextCtrl.h delete mode 100644 GUICommon/PortTextCtrl.cpp delete mode 100644 GUICommon/PortTextCtrl.h delete mode 100644 GUICommon/RestrictedTextCtrl.cpp delete mode 100644 GUICommon/RestrictedTextCtrl.h delete mode 100644 MakefileGUI delete mode 100644 MakefileGUIPi delete mode 100644 MakefilePi delete mode 100644 WindowsUSB/dvrptr_cdc.inf delete mode 100644 WindowsUSB/gmsk.cat delete mode 100644 WindowsUSB/gmsk.inf delete mode 100644 WindowsUSB/xDVRPTR-32-64-2.inf delete mode 100644 linux/dstarrepeater.example create mode 100644 schema.json diff --git a/.gitignore b/.gitignore index 5b78d6c..42f3117 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ Release .vs *.a *.d +tmp/ diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000..13b9e74 --- /dev/null +++ b/BUILD.md @@ -0,0 +1,133 @@ +# Building DStarRepeater + +## Linux + +### Dependencies + +| Package | Ubuntu / Debian | Alpine | Purpose | +|---------|-----------------|--------|---------| +| C++17 compiler | `g++` (GCC 13+) | `g++` | Compilation | +| GNU Make | `make` | `make` | Build system | +| ALSA dev | `libasound2-dev` | `alsa-lib-dev` | Sound card modem | +| libusb 1.0 dev | `libusb-1.0-0-dev` | `libusb-dev` | USB modem (GMSK) | +| Mosquitto dev | `libmosquitto-dev` | `mosquitto-dev` | MQTT telemetry (on by default) | +| Linux headers | — | `linux-headers` | Required on Alpine only | + +**Raspberry Pi (additional):** + +| Package | Install | Purpose | +|---------|---------|---------| +| wiringPi | `sudo apt-get install wiringpi` or [build from source](https://github.com/WiringPi/WiringPi) | GPIO controller (on by default on ARM) | + +### Install (one-liner) + +**Ubuntu / Debian:** +```bash +sudo apt-get install g++ make libasound2-dev libusb-1.0-0-dev libmosquitto-dev +``` + +**Raspberry Pi:** +```bash +sudo apt-get install g++ make libasound2-dev libusb-1.0-0-dev libmosquitto-dev wiringpi +``` + +**Alpine:** +```bash +apk add g++ make alsa-lib-dev libusb-dev linux-headers mosquitto-dev +``` + +### Build + +By default, `make` produces a release build with MQTT enabled and GPIO auto-detected (on for ARM). + +```bash +make # default: release, MQTT on, GPIO auto +make MQTT=0 # without MQTT (no libmosquitto-dev needed) +make GPIO=0 # without GPIO (no wiringPi needed) +make BUILD=debug # debug build with symbols and assertions +``` + +### Install + +```bash +sudo make install +``` + +This installs: +- `dstarrepeaterd` to `/usr/bin` +- AMBE voice beacon data to `/usr/share/dstarrepeater/` +- Example config to `/etc/dstarrepeater/dstarrepeater.ini.example` + +--- + +## Windows + +The source includes `#if defined(_WIN32)` blocks throughout. The provided `Makefile` targets Linux only — you will need a Visual Studio project or CMakeLists.txt. + +### Compiler + +| Option | Requirement | +|--------|-------------| +| Visual Studio 2019+ | Enable `/std:c++17` in project properties | +| MinGW-w64 (MSYS2) | `g++ -std=c++17` | + +### Libraries + +| Library | Source | Required? | +|---------|--------|-----------| +| Windows SDK | Included with Visual Studio | Yes — provides Winsock2, serial port, and console APIs | +| [libusb 1.0](https://libusb.info/) | Pre-built Windows binaries available | Yes — GMSK modem support | +| [PortAudio](http://www.portaudio.com/) | Build from source or use vcpkg | Yes — sound card modem (replaces ALSA) | +| [Eclipse Mosquitto](https://mosquitto.org/) | Installer or vcpkg | Optional — only for MQTT builds | + +### Linker flags + +``` +ws2_32.lib Winsock2 (sockets) +portaudio.lib Sound card modem +libusb-1.0.lib GMSK modem +mosquitto.lib MQTT (optional) +``` + +### Platform notes + +- Serial ports use `CreateFile` / `ReadFile` / `WriteFile` (Win32 API) +- Sockets use Winsock2 with automatic `WSAStartup` / `WSACleanup` +- Signal handling uses `SetConsoleCtrlHandler` (Ctrl+C, close events) +- Single-instance enforcement uses a named mutex (no PID file) +- K8055 (Velleman) and URI USB controllers are stub-only on Windows — they require vendor DLLs that are not yet integrated + +--- + +## macOS + +The source includes macOS support for endian handling (`OSByteOrder.h`), serial ports (POSIX `termios`, same as Linux), and PortAudio audio. The `Makefile` currently targets Linux only. + +### Dependencies (Homebrew) + +```bash +xcode-select --install +brew install libusb portaudio +``` + +For MQTT support: +```bash +brew install mosquitto +``` + +### Building + +The Makefile needs adjusted compiler flags and Homebrew library paths for macOS. A minimal approach: + +```bash +export CFLAGS="-std=c++17 -O2 -Wall -I$(brew --prefix)/include" +export LDFLAGS="-L$(brew --prefix)/lib" +# then adjust the Makefile or build manually +``` + +### Platform notes + +- Endian conversion uses `` (via `EndianCompat.h`) +- Sound card modem uses PortAudio (same as Windows) +- Serial ports use POSIX `termios` (same as Linux) +- ALSA is not available — sound card modem requires PortAudio diff --git a/BUILD.txt b/BUILD.txt deleted file mode 100644 index cc07e4f..0000000 --- a/BUILD.txt +++ /dev/null @@ -1,70 +0,0 @@ -Repeater - 20180710 -=================== - -Windows -------- - -To use the Repeater control software you will first need to build the latest -version of wxWidgets (http://www.wxwidgets.org), the version I used was 3.0.4. -I also installed it in the default location which is C:\wxWidgets-3.0.4. - -You will also need a copy of PortAudio (http://www.portaudio.com), I used the -latest stable version and it appears to be fine. I put that into the -"Visual Studio 2017\Projects" folder alongside the source code for the -Repeater itself. - -For compiling I use Visual C++ 2017 Community Edition downloaded from Microsoft -for free. I recommend that you use the same. - -To build wxWidgets, you simply need to open Visual Studio 2017 using the File -> -Open -> Projects/Solutions and load the wx_vc12.sln file to be found in -wxWidgets-3.0.4\build\msw directory and then go into Batch Build and select the -DLL Debug and DLL Release entries for every one, this take a little time! Then -build them. - -Unfortunately building the software under Windows also requires the downloading -of a very large file from Microsoft, the WDK. There is no easy way around this -but once installed it can be forgotten about. - -The path names for things like wxWidgets and PortAudio are embedded within the -Solution and Project preferences, and will need changing if anything other than -these default locations are used. The first pass through the compiler will no -doubt tell you all that you need to know if there are problems. - -Once you have built the executables, you will need to copy the correct portaudio -and wxWidgets files to the same location as the executables. For 32-bit systems -these are portaudio_x86.dll, wxbase30u_vc_custom.dll, wxmsw30u_adv_vc_custom.dll, -and wxmsw30u_core_vc_custom.dll. On 64-bit systems you'll need portaudio_x64.dll, -wxbase30u_vc_x64_custom.dll, wxmsw30u_adv_vc_x64_custom.dll, and -wxmsw30u_core_vc_x64_custom.dll - -If you are running in debug mode then the required wxWidgets files have the names -xxx30ud_xxxx instead. These can be found in the wxWidgets-3.0.4\lib\vc_dll -directory. - -It is also probable that you'll need to install a copy of the latest Visual C++ -run-time libraries from Microsoft, if you are not running the repeater software -on the same machine as the development/compilation was done on. You can find the -latest versions at https://support.microsoft.com/en-gb/help/2977003/the-latest-supported-visual-c-downloads - -Linux ------ - -You need to ensure that wxGTK, ALSA and libusb-1.0 are already -installed on your machine, under Ubuntu these are available from the standard -repositories, the version of wxWidgets is adequate. However you should get the -latest version of PortAudio from its home and build it from scratch. - -To install them from scratch, you need to get wxGTK from -, and libusb-1.0 from . -If you do a "make install" on all of them then they'll be installed in the -right places and nothing more needs to be done. - -On the Raspberry Pi you will need to build and install the wiringPi GPIO library -which is available from . -This will allow use the GPIO pins as controller pins for the different repeaters -if needed. - -To actually build the software, type "make" in the same directory as this file -and all should build without errors, there may be a warning or two though. Once -compiled log in as root or use the sudo command, and do "make install". diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..947c59c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1757 @@ +# Changelog + +Newest changes first. + +## 2026-03-19 + +- Removed wxWidgets dependency; ported to standalone C++17 +- Added cross-platform support (Linux, Windows, macOS) +- INI-style config file format (MMDVMHost compatible) +- Config-driven logging with file, display, and MQTT level controls +- Config file path as required command-line argument +- MQTT enabled by default; GPIO auto-detected on ARM Linux +- Comprehensive code quality, security, and threading safety fixes +- Restored DTMF command execution via system() +- Fixed getControl() parameter ordering bug (commands 3-6 swapped) +- Added missing GMSK interface type config parsing +- Full codebase documentation and comments + +## 2018-07-10 + +- Add error recovery to the Icom Terminal and Access Point modes. + +## 2018-07-03 + +- Add Icom Terminal and Access Point modes. + +## 2018-05-10 + +- Port to wxWIdgets-3.0.x + +## 2015-10-12 + +- Change to the Windows serial controller for Windows 10. +- Add overflow warnings for the MMDVM. +- 2015xxxx +- Fix the TX Delay setting for the MMDVM. + +## 2015-10-01 + +- Added the RX and TX levels to the MMDVM driver and configuration. +- Change the slow data header replacement in Gateway mode to be more intelligent. +- Add sync regeneration to the sound card repeater driver. +- Change the slipped sync detection in the sound card repeater driver. +- Use a new matched filter in the GMSK modem. +- More MMDVM driver changes. + +## 2015-07-09 + +- Allow for a late arriving data sync in the sound card repeater controller. +- Add support for D-Star operation with the MMDVM. +- Fix bugs in the DV-RPTR V1, V2, V3, and DVMEGA drivers. + +## 2015-06-15 + +- Rearrange the source code slightly. +- Allow a DVMEGA to be even slower at responding the first time. + +## 2015-04-04 + +- Added method to ALSA interface for TX control in the sound card controller. + +## 2015-03-08 + +- Added a .mk file for the Pi2. +- Increased the network timeout from one second to two seconds. +- More changes to the ALSA interface. + +## 2015-02-13 + +- Updated the ALSA handling once again. +- Fixed a compile bug when defining GPIO. + +## 2015-02-10 + +- Updated the dynamic serial port selection for /dev/ttyACM* +- Updated the ALSA handing. + +## 2015-02-08 + +- Added ALSA for use on Linux, PortAudio is used elsewhere. +- Dynamic serial port selection on Linux. +- Allow for cross-band operating on the DVMEGA. +- GPIO is potentially available on more platforms. +- Assert the RTS pin for the DVMEGA at startup. +- Getting the version at the startup of a DVMEGA is now more aggressive. +- Added a sanity check to the reply from the DVMEGA. +- Improved configuration and validation for the DVMEGA. + +## 2014-05-21 + +- Added DVAP error dump. + +## 2014-05-20 + +- Switch on internal and external speakers for XDVRPTR firmware/hardware. +- DVMEGA messages say DVMega now. +- Added power setting to the DVMEGA radio. +- Add extra TX and RX entries for the Split driver, but not in the GUI. +- Add two extra commands. + +## 2014-04-24 + +- Added /dev/ttyACM* for all serial devices under Linux. +- Added PTT Inversion control for external hardware controllers. +- Hold off the next D-Star transmission until driver is clear of previous data. + +## 2014-04-15 + +- Change the DV-RPTR V2 & V3 handling back to the way it is done in the DV-RPTR Repeater. +- Fixed watchdog timer handling in TX Only and TX and RX modes. +- Reduce buffering in TX Only and TX and RX modes for local connections. + +## 2014-04-10 + +- The statistics in the Split driver include all configured receivers. +- Put the type of the repeater into the title bar of the GUI. +- The first registration packet is after 10 seconds, and 30 seconds thereafter. +- Fix many bugs in the Split driver. + +## 2014-03-06 + +- Removed the modem watchdog. +- Fixed the DV-RPTR V2 and V3 modem drivers. +- Fixed the DVMEGA driver. +- Fix the network name going missing the config program. + +## 2014-03-03 + +- Experimental DC offset removal in the sound card driver. +- Use names in the Split driver to allow for easier matching of addresses and ports. +- Increase the number of receivers in the Split driver to five from three. +- Added a modem watchdog to the DV-RPTR, DVAP and DVMEGA modem drivers. +- Hold off writing the header until the TX is off for the DV-RPTR and DVMEGA modems. + +## 2014-02-17 + +- Add extra logging to the Split driver. +- Remove the FEC restoration in RX only and RX and TX modes. +- Added TX Tail to the sound card driver. +- Changed the timings in the GMSK driver. +- Added GMSK driver modem reopen. +- Write the configuration to a file under Windows. + +## 2014-02-08 + +- GMSK Modem changes. +- Improve the DVAP transmit handling. +- Added the Split controller. + +## 2014-01-29 + +- Move the D-Star Repeater to its own package. +- More GMSK Modem changes for better locking. + +## 2014-01-28 + +- Tweaks to the GMSK Modem controller in the D-Star Repeater. +- More work on buffering in the D-Star Repeater for all modem types. + +## 2014-01-18 + +- Fix endian-ness of the DVMEGA frequency command. + +## 2014-01-17 + +- Removed the DVAP Node and DV-RPTR Repeater. +- Block Australian foundation class and French novice callsigns from using the repeaters. +- Removed the optional delay on the DV-RPTR V1 modem. +- Added the DVMEGA hardware. + +## 2013-12-31 + +- More cleanups. +- Removed the white list functionality. +- Added explicit support for the DV-RPTR V3 modem. + +## 2013-09-04 + +- Remove even more of the C++ changes. + +## 2013-09-03 + +- Removed the DC blocker from the sound card based repeaters. +- Remove some of the C++ changes. + +## 2013-08-31 + +- Make DV-RPTR V2 handling better. +- Catch exceptions and other C++ changes in the D-Star Repeater. +- Add TX Delay to the DV-RPTR V2 modem in the D-Star Repeater. + +## 2013-07-26 + +- Restore D-Star Repeater window position on startup. +- Allow for the error reply to be disabled. + +## 2013-07-02 + +- Added daemon info to the log. +- Changes to the internal buffering. +- Remove the Analogue Repeater from the Repeater package. +- Remove the DCSGateway, DExtraGateway, and ParrotController from the Repeater package. +- Add a DC blocker to the sound card GMSK demodulator. +- Implement GMSK PLL locking. + +## 2013-06-22 + +- Added XRF310. +- Try overlapped serial I/O under Windows again. +- Add the serial port PTT control to the D-Star Repeater. +- Add /dev/ttyS* devices to the serial control list. +- Added a receive level setting to the sound card driver in the D-Star Repeater. +- Fixed bugs in the D-Star Repeater regarding end of incoming transmissions. + +## 2013-06-15 + +- Added XRF250. +- Add an optional startup delay for the DV-RPTR V1. +- Add a sound card interface to the D-Star Repeater. + +## 2013-05-23 + +- Fixed GMSK Modem bug in the D-Star Repeater. +- Updated XRF008s IP addresses. +- Added XRF727. + +## 2013-05-15 + +- Removed aggressive DVAP serial port opening. +- Remove support for the DUTCH*Star 0.1.00 firmware. +- Add GMSK modem support to the D-Star Repeater. +- Added Arduino hardware support. + +## 2013-05-08 + +- Revert most of the serial port changes. + +## 2013-05-03 + +- Tweaks to the DVAP handling. +- Made opening the serial port more aggressive for the DVAP. +- Included the D-Star Repeater and Config program in the Windows package for the first time. + +## 2013-04-23 + +- Change to the wiringPi library rather than bcm2835 library for controlling the RPi. +- Added the generic D-Star Repeater and configuration program. +- Allow incoming radio audio during the valid_wait period. +- Don't reset the announcement timer on transmissions. +- Added XRF851. + +## 2013-03-23 + +- Updated XRF003s IP address. +- Added the recording and playback of announcements. + +## 2013-03-15 + +- Added XRF038. +- Allow for the new format of Danish callsigns. + +## 2013-03-09 + +- Allow STN* callsigns as the my callsign. +- Allow for switching DTMF blanking off for duplex. + +## 2013-03-02 + +- Make the DVAP initialisation less aggressive. +- Updated to match ircDDB Gateway 20130302. +- Added XRF150 + +## 2013-02-19 + +- Updated XRF008s and XRF123s IP addresses. +- Reset the DVAP into GMSK mode if FM data is received. +- Fix for GMSK modems request for space. +- Added DCS021. +- Added XRF333. + +## 2013-02-03 + +- Remove DVAP frequency offset setting. + +## 2013-01-31 + +- Upgrade the DCS Gateway to the latest protocol version. +- Revert DVAP polling change. + +## 2013-01-27 + +- Allow for temporary ack text. +- Convert the DCS hosts files to use hostnames. +- Increase DVAP polling speed to increase reliability on slow machines. +- Fixed bug in status reporting. + +## 2013-01-11 + +- Make the frame wait time configurable in the Split Repeater. +- Allow for empty beacons in the Analogue Repeater to suppress them. +- Convert old DV-RPTR configuration values (Windows only). +- Added DCS019. + +## 2012-12-20 + +- Added DCS020. +- Updated XRF008s IP address. +- Add Ethernet support for the DVRPTR-V2 hardware. +- Add support for the Raspberry Pi GPIO port. + +## 2012-11-12 + +- Removed XRF018 and XRF250. +- Updated DCS007s IP address. +- Added new XDVRPTR data transfer mode for v0.72 firmware or later. + +## 2012-10-27 + +- Added DCS016, DCS017, and DCS018. +- Changed the announcement audio for B and D for UK English. +- Removed the beacon in Gateway mode. + +## 2012-10-04 + +- Put the DCS and DExtra Gateways back into the package. +- Updated DCS protocol. +- Added support for the 70cms DVAP. +- Use the DVAP to validate its own frequency. +- Add a pause in the GMSK Repeater before PTT is set to on. + +## 2012-09-20 + +- Make LibUsb on both Windows and Linux more agressive at transferring data. +- Remove the need for the extra thread in the Linux command line versions. +- Initial support for the XDV-RPTR (V2) protocol. +- Removed the DCS and DExtra Gateways. +- Added SVN revsion version to the log file. +- Add AMBE logging to non-GUI versions of the repeaters. +- Add -audiodir command line option to change the location of the AMBE data logging. + +## 2012-09-01 + +- Allow module E on all of the repeaters. +- Re-added Windows LibUSB support to the GMSK Repeater. + +## 2012-08-26 + +- Upgraded to Visual C++ 2010 on Windows. +- Platform dependent settings moved to Makefile includes. +- Changed the D-Star Repeater to the Sound Card Repeater. +- Upgraded to the latest version of the DCS protocol. +- Increased the default TX delay in the DV-RPTR Repeater to 150ms from 30ms. +- 20120826a +- Reverted to using Visual C++ 2008 on Windows. + +## 2012-08-02 + +- Added DCS014 and DCS015. +- Removed XRF100. +- Fixed ARM cross compilation location of data files. +- Changed silence insertion parameter. +- Allow for up to four commands. + +## 2012-07-06 + +- More GMSK Repeater rollbacks. +- Allow cross compilation to ARM processors. +- Added DCS011. + +## 2012-06-15 + +- Added DCS013. +- Roll back GMSK repeater changes regarding driving the modem. +- Fixed background data in the GMSK Repeater. + +## 2012-06-04 + +- Remove the GMSK debugging statements. +- More GMSK repeater changes. +- Fix bug in TX and RX mode in all but the D-Star Repeater. + +## 2012-06-03 + +- Lot of debugging statements in the GMSK drivers. +- More GMSK repeater changes. +- Removed XRF009, XRF010, XRF011, XRF030, XRF038, and XRF110. +- Updated XRF031s and XRF069s IP address. +- Added DCS012. + +## 2012-06-02 + +- Remove the LibUsb option for the GMSK modem in Windows. +- Infill data gaps with the last transmitted audio or silence. +- More GMSK repeater changes. + +## 2012-05-27 + +- Improvements to the changes made in 20120526. +- 20120527a +- Minor tweaks to 20120527 relating to Windows mostly. +- Not generally released. + +## 2012-05-26 + +- Run the GMSK Modem in a seperate thread. + +## 2012-05-24 + +- Upgraded network error reporting. +- Added DCS010. +- Removed debug statements from the Slit Repeater. +- Add One-Touch Reply to the Dummy Repeater. + +## 2012-05-11 + +- Fix the -nolog option for the Dummy Repeater. + +## 2012-04-28 + +- Small revision to the DVAP driver. +- Add the -nolog command line option to the Dummy Repeater. + +## 2012-04-24 + +- Remove the architecture setting in the top level Makefile. +- Added DCS004. + +## 2012-04-20 + +- Upgrade the reopening of the DV-RPTR modem on Linux. +- Remove the first poll from the repeaters to the gateway. +- 20120420a +- Tweaks to the GMSK modem LibUSB driver to reduce the logging. +- Changes to the GMSK modem WinUSB driver. + +## 2012-04-19 + +- Allow a greater range of GMSK modem addresses. +- Replaced NAtools with direct calls to WinUSB :-) +- Handle reopening of the DV-RPTR modem if it disconnects, Linux only for now. + +## 2012-04-14 + +- Added DCS007. +- Handle reopening of the GMSK modem if it disconnects. + +## 2012-04-12 + +- Fixed BER display on all of the repeater GUIs. +- Fixed header transmission bug in diversity modes. +- More changes to the Split Repeater. +- Not generally released. + +## 2012-04-11 + +- Added Norwegian as a language to the DExtra and DCS Gateway. +- Added an optional voice beacon to all of the D-Star repeaters. + +## 2012-04-03 + +- Sort the callsign entries in the Dummy Repeater. +- Minor cleanups. + +## 2012-04-02 + +- Better error handling of the DV-Dongle. +- More changes to the Split Repeater. + +## 2012-03-30 + +- Updated the DCS and DExtra hosts files to the latest versions. +- Convert the main log files to use UTC instead of local time. +- More changes to the Split Repeater. + +## 2012-03-28 + +- Fixed ack bug in the Split Repeater. +- Added DCS006. + +## 2012-03-27 + +- Add extra logging to the Split Repeater for unknown IP addresses and ports. +- Modify the Linux configuration system to fix a bug. +- Updated XRF017s IP address. +- Added DCS008. + +## 2012-03-26 + +- Fixed bugs in the RF D-Star repeaters when used with the Split Repeater. +- Fixed bugs in the use of the Split Repeater with the RF repeaters. +- Added DCS005. + +## 2012-03-24 + +- Added "TX and RX" mode to all of the D-Star repeaters except the DVAP Node. +- More changes to the Split Repeater. +- Added DCS009. + +## 2012-03-22 + +- Added XRF030. +- Added the Split Repeater. +- Allow minimum ack time in the Analogue Repeater to be 100, 300, and 500ms. +- Add up to 100ms of squelch delay in the Analogue Repeater. + +## 2012-03-20 + +- Updated XRF021s IP address. +- Added TX output to the K8055 with the GMSK and DV-RPTR repeaters. +- Added the DCS Gateway. + +## 2012-03-12 + +- Added CM119A product id for latest URI USB hardware. +- Added K8055 support to the GMSK and DV-RPTR repeaters. + +## 2012-03-10 + +- Updated the DV-RPTR driver again. +- Binary only release for Windows users. +- 20120310a +- Updated the DV-RPTR and DVAP drivers. + +## 2012-03-09 + +- Changes to support the DCS reflector system. +- More changes to the DV-RPTR driver. + +## 2012-03-08 + +- Changes to the DV-RPTR driver. +- Updated XRF021s and XRF044s IP address. +- Added XRF222, XRF353, and XRF777. +- Not generally released. + +## 2012-03-03 + +- Added AMBE regeneration to background data. +- Added receive-only and transmit-only modes to the repeaters. + +## 2012-02-28 + +- Restore GUI window position on startup. +- New repeater to gateway protocol implemented. +- Allow background audio data to be transmitted. +- Added a receive hang time to the Dummy Repeater. + +## 2012-02-23 + +- Blank any DTMF tones from being transmitted over RF. +- Added audio delay and pre-, de-emphasis to the Analogue Repeater external port. + +## 2012-02-21 + +- Changed the lower frequency limits of the analogue repeater callsign and ack. +- Added the E (Echo) and I (Info) commands to the DExtra Gateway. +- Updated XRF069s IP address. + +## 2012-02-13 + +- Fixed D-Star Repeater Windows configuration bug. + +## 2012-02-10 + +- Updated XRF021s IP address. +- Small cleanups. + +## 2012-02-06 + +- Bug fixes for 20120205. + +## 2012-02-05 + +- New configuration system for non-Windows platforms. +- Added XRF040. + +## 2012-01-25 + +- Allow for the settings of the beacon text for the D-Star repeaters. +- Equalised the audio levels in the Dummy Repeater; +- Updated XRF019 IPs address. +- Added XRF110. + +## 2012-01-16 + +- Updated XRF033s IP addeess. +- Fixed GUI PTT bug in the Dummy Repeater. +- Add an audio resampler to the Dummy Repeater. + +## 2012-01-11 + +- Increase maximum D-Star Repeater hang time to 3000ms from 2000ms. +- Improve gateway mode rejection rules. +- Allow the Dummy Repeater to be controlled from external hardware. +- Added XRF119. + +## 2011-12-31 + +- Added the upgraded Dummy Repeater to the repeater package. +- Allow channel D in the DExtra Gateway. + +## 2011-12-22 + +- Allow the ack on the D-Star repeaters to be selectable for BER or status. + +## 2011-12-21 + +- More work on the Analogue Repeater radio audio to the external port. + +## 2011-12-20 + +- More work on the Analogue Repeater radio audio to the external port. +- Updated XRF031s IP address. + +## 2011-12-15 + +- More work on the Analogue Repeater radio audio to the external port. + +## 2011-12-13 + +- More work on the Analogue Repeater radio audio to the external port. + +## 2011-12-12 + +- In the Analogue Repeater send radio audio to the external port when relaying from the external port. +- Removal of the USAGE.txt file. +- Fix an ancient bug with the output switching in the D-Star Repeater. + +## 2011-12-09 + +- Change the ack text to include the reflector instead of the time. +- Updated XRF019s IP address. + +## 2011-12-08 + +- Fix bug in the D-Star Repeater for the new status messages. +- Similar fix to the DVAP Node. +- Small change to the new ack format. + +## 2011-12-07 + +- Acks changed from the link status to the transmission time and BER. +- Allow up to five status messages. + +## 2011-12-04 + +- Allow headers received when in full duplex and transmitting network data to be passed to the gateway. + +## 2011-11-26 + +- Changes to the DV-RPTR modem startup for slow serial subsystem starts. +- Dummy gateway handling added. + +## 2011-11-25 + +- Handle startup of the DV-RPTR modem and DVAP better. + +## 2011-11-22 + +- Allow more choices for the analogue repeater Ack delay. +- Cleaned up the DV-RPTR Modem settings. +- Updated the Windows NATools DLL. +- Updated XRF002 IP address. +- DExtra Gateway fix. + +## 2011-11-20 + +- Changes to the DVAP Node and DV-RPTR Repeater handling. + +## 2011-11-18 + +- Increased the GUI width on Linux to accomodate the new status messages. +- Allow for the DV-RPTR TX Delay up to 500ms from 250ms. +- Fixed the config file name for the DV-RPTR daemon version. +- Fixed the DV-RPTR communications protocol. +- Fixed DV-RPTR version string. +- Updated XRF069 IP address. +- Added XRF038. + +## 2011-11-12 + +- Fix to the DExtra Gateway. +- Minor bug fix. + +## 2011-11-09 + +- Output buffer changes. +- Added support for the reporting of the two status messages. + +## 2011-11-02 + +- More changes to the DV-RPTR protocol. +- The default log directory is now the home directory under Linux. +- Bug fix to the DExtra Gateway. +- Increased network security for the repeaters. +- Added XRF123. + +## 2011-10-22 + +- Changes to the DV-RPTR protocol. +- Added packet logging for local traffic for all DV repeaters. +- Pass the headers from the DV repeaters through properly. +- Increase the size and number of the network queues. +- Changed XRF069 IP address. +- Changes to the repeater to gateway protocol. + +## 2011-10-16 + +- Added XRF069. +- Reverted external input handling for the Analogue Repeater. + +## 2011-10-04 + +- Changed how data is fed to the DVAP and DV-RPTR. +- Incorporated the new DV-RPTR data formats. + +## 2011-09-23 + +- More changes to the DV-RPTR Repeater. +- Changed XRF073 IP address. + +## 2011-09-15 + +- Revert the FTDI change for the DVAP on Windows. +- Changes to the queueing. +- Enable the debug flag on Linux builds. +- More changes to the DV-RPTR Repeater. + +## 2011-09-13 + +- Use the official FTDI interface to access the DVAP on Windows. +- Changed XRF002 IP address. + +## 2011-09-07 + +- More internal changes to handle extreme situations. + +## 2011-09-05 + +- Considerable internal changes to the output queueing. +- Bug in the silence inseration fixed. + +## 2011-09-02 + +- Seperate the closdown ID from the periodic ID in the Analogue Repeater. +- Lots of internal changes to the DVAP Node. +- More work on the DV-RPTR Repeater. + +## 2011-08-31 + +- More work on the DV-RPTR Repeater. + +## 2011-08-30 + +- Fix Squelch and Noise display when transmitting in the D-Star Repeater. +- First untested version of the DV-RPTR Repeater. + +## 2011-08-26 + +- Allow for alternative GMSK Modem addresses in NAtools. +- Use the new silence insertion in the D-Star Repeater. + +## 2011-08-25 + +- Added a noise level display to the D-Star Repeater. +- Added the Squelch Mode to the D-Star Repeater. + +## 2011-08-23 + +- Fixed a bug in the DExtra Gateway. +- Allow the opening callsign of the Analogue Repeater to be different from the closing callsign. +- Fixed the minimum time for ack timer in the Analogue Repeater. +- Added XRF073. + +## 2011-08-22 + +- The Battery Ack is now configurable in the Analogue Repeater. +- Fix to the Windows SerialLineController.cpp +- Add more buffering to the D-Star Repeater. + +## 2011-08-18 + +- The RX and TX Levels affect the D-Star Repeater in real-time. +- The levels and thresholds in the Analogue Repeater operate in real-time. +- Increased the Analogue Repeater hang timer maximum to 30 seconds. +- All of the IDs and Acks in the Analogue Repeater can now be WAV files. + +## 2011-08-16 + +- Allow the timers to handle long timeouts correctly. +- The Squelch Level affects the D-Star Repeater in real-time. +- Change the serial port handling for the Analogue and D-Star repeaters. + +## 2011-08-14 + +- Added the noise squelch to the D-Star Repeater. +- More work on GMSK full duplex mode. + +## 2011-08-11 + +- Minor internal changes to all of the repeaters. +- Improved timing for the DExtra Gateway and Parrot Controller. +- Added fuzzy matching for the frame sync to the D-Star Repeater. +- 20110811a +- Fix for GMSK Repeater full duplex mode. +- Identify the OS and wxWidgets version in the log for all programs. + +## 2011-08-08 + +- First version of the DVAP Node program. +- Allow incoming network data during wait periods. +- Clean up GMSK Repeater local repeating. +- 20110808a +- Serial port selection bug fixed. +- 20110808b +- Windows serial port changes. + +## 2011-07-20 + +- Fix PTT bug in 20110719. + +## 2011-07-19 + +- Removed the Network Delay from the D-Star Repeater. +- Added extra delay and buffering to the D-Star Repeater for network traffic. + +## 2011-07-17 + +- Cleans-ups in all the repeaters. +- More work on 0.1.00 stability. + +## 2011-07-16 + +- Added Nederlands as a language option to the DExtra Gateway. +- Removed the cycle time option for the GMSK Repeater. +- First version that is stable with DUTCH*Star 0.1.00 firmware. + +## 2011-07-14 + +- Insert a 100ms delay before sending the data after sending the header. +- Updated XRF033. + +## 2011-07-12 + +- Improve the clock accuracy in the GMSK Repeater. + +## 2011-07-11 + +- Handle DUTCH*Star 0.1.00 firmware explicitely. + +## 2011-07-10 + +- Turned the logging upside down to match the gateway, and common sense. +- Improvements to the NAtools drivers. + +## 2011-07-08 + +- Upgraded the LibUSB driver from libusb-0.1 to libusb-1.0 under Linux. +- Upgraded the NAtools version under Windows. +- Removed NAtools on the Linux side. + +## 2011-07-06 + +- Changed timings when dealing with LibUSB and relaying from the modem. +- Changed Modem Type to Firmware Type. + +## 2011-07-04 + +- Allow for compilation without NAtools being available. +- Added XRF028. + +## 2011-07-03 + +- Trying to handle unreasonable combinations of modem and USB chipset. + +## 2011-07-02 + +- Remove the need to have LibUSB installed on Windows. + +## 2011-06-28 + +- Remove the need to have NAtools installed on Windows. + +## 2011-06-22 + +- Clean ups and removal of some logging messages. +- Added extra delays for libUSB. + +## 2011-06-21 + +- Increased the max cycle time value to 20ms. +- Now write 12 bytes per cycle instead of 8. +- 20110621a +- Changed from the broken node_putdta to node_io. + +## 2011-06-13 + +- Display cycle overruns more intelligently. +- Display time taken for the networking to complete. +- Stop polling the URI USB in the D-Star Repeater, it's not needed. + +## 2011-06-10 + +- Added more cycle times for the GMSK modem. +- Added an extra serial port config. + +## 2011-06-09 + +- Add the cycle time setting to the GMSK modem settings. +- Added different serial port configs to allow for different serial port settings. + +## 2011-06-01 + +- Small GMSK modem change, rolls back another change made in 20110529. + +## 2011-05-29 + +- Tweaks to the software type/version reporting to the gateway. +- 20110529a +- Small GMSK modem change, rolls back a change made in 20110529. + +## 2011-05-25 + +- Updated the IP address of XRF020. +- Changed poll format to report the repeater type to the gateway. +- Small change in timing of the GMSK Repeater. + +## 2011-05-12 + +- Updated the USAGE.txt file to include the -daemon option. +- The pid of the programs is written to /var/run for Linux versions. +- Added the -confdir option to the daemon versions of the programs. + +## 2011-05-10 + +- Fundamental change to the GMSK Modem handling. +- Cleaned up 20110508. + +## 2011-05-08 + +- Added -daemon option to create proper daemons on Linux. +- Not generally released. + +## 2011-05-06 + +- Added XRF031. +- Added the ack time to the minimum time for the analogue repeater controller. +- Allow outgoing DExtra links to have random ports. +- Change timings in the GMSK Repeater. + +## 2011-04-21 + +- Fixed an obscure bug in the Parrot Controller. +- Updated the IP address of XRF021. +- Change timings and data feed to the GMSK Modem. + +## 2011-04-20 + +- Changed the timing of feeding the data to the GMSK Modem. + +## 2011-04-18 + +- Added XRF033. +- Updated the IP address of XRF013. +- Changed the timing in the main loop. +- Not generally released. + +## 2011-04-17 + +- Changed from wxWidgets 2.8.11 to 2.8.12. +- Small internal timing tweak for the GMSK Repeater. + +## 2011-04-13 + +- A small tidy up of the previous release. + +## 2011-04-11 + +- Different method of silence insertion used. + +## 2011-04-04 + +- More mutex locking changes in the GMSK Repeater. +- Timing changes in the GMSK Repeater. + +## 2011-04-03 + +- Mutex locking in the last couple of releases was a little wrong. + +## 2011-04-02 + +- Refinements to 20110331 based on feedback. + +## 2011-03-31 + +- Timing changes in the GMSK Repeater. +- Put the networking back into the second thread on the GMSK Repeater. + +## 2011-03-29 + +- Relax the header checksum testing under certain circumstances. +- Check the incoming my call with a regular expression. +- More buffering of network data in the GMSK Repeater. + +## 2011-03-27 + +- Removed the bias patch which was added to the 20110308 release. +- Removed the silly GMSK Repeater error messages that I added. +- Updated the IP address of XRF008. +- Added extra my callsign validation to the repeaters. + +## 2011-03-08 + +- Added daily logs. +- Added the -logdir command line option to the programs. +- Added XRF007 and XRF021 to the DExtra_Hosts.txt file. +- Added the sound card demodulator bias patch from Eric NS7DQ. +- 20110308a +- Timing changes to the GMSK Repeater closer to the settings of 20110219a. + +## 2011-03-01 + +- Remove a delay added to the NAtools driver. +- Fixed Linux compile error with the previous release. + +## 2011-02-27 + +- Added optional RPT1 validation. +- Relaxed the timing when talking to the GMSK modem. + +## 2011-02-22 + +- Added Swedish and Spanish translations to the DExtra Gateway. +- Increased the USB timeout when using libUSB with the GMSK modem. +- Increased the packet silence timeout from 30ms to 40ms. +- Run the network processing from the timer thread in the GMSK Repeater. +- 20110222a +- Small change to the GMSK Repeater to stop lock-ups. + +## 2011-02-19 + +- Swapped the callsigns in the D-Star Repeater to match the GMSK Repeater. +- Removed support for Dutch*Star 1.00.0 firmware. +- 20110219a +- Re-arranged the transmitted callsigns in both of the digital repeaters. +- Validate the incoming network header for both digital repeaters. + +## 2011-02-17 + +- Fixed bug in DExtraGateway with incoming headers disrupting communications. +- Change the maximum Parrot Controller beacon timer to 20 minutes. +- More GMSK Repeater internal changes. + +## 2011-02-15 + +- Added XRF020 to DExtra Gateway. +- Added Dutch*Star 1.00.0 firmware compatibility again. + +## 2011-02-13 + +- Internal restructuring of the GMSK Repeater. +- Removed the RX State from the GMSK Repeater GUI. + +## 2011-02-10 + +- Handle errors in the GMSK modem better. +- More timing changes to the GMSK Repeater. + +## 2011-02-08 + +- More timing changes to the GMSK Repeater. + +## 2011-02-05 + +- Fixed the callsigns in the acks of the GMSK Repeater. +- Change the timings, particularly when handling receive on the modem. +- Change the default ports to match those of ircDDB Gateway. + +## 2011-01-23 + +- More timing changes for the GMSK Repeater. + +## 2011-01-21 + +- More timing changes for the GMSK Repeater. + +## 2011-01-20 + +- Added XRF000 for the DExtra Gateway. +- DExtra protocol has been upgraded. +- Another timing change for the GMSK Repeater receiver. + +## 2011-01-16 + +- Swap RPT1 and RPT2 for the NAtools modem driver. +- Increase the polling rate in the GMSK Repeater. +- Not generally released. + +## 2011-01-14 + +- Swap RPT1 and RPT2 for the libUSB modem driver. +- Different GMSK modem buffer filling routines. +- Not generally released. +- 20110114a +- Same as 20110114 but with additional debugging information. +- Not generally released. + +## 2011-01-09 + +- Allow for the use of both the libUSB and NAtools interface. +- Change how the internal timing operates. +- Not generally released. +- 20110109a +- Increase the buffer size and put in a cycle of silence to start it off. +- Not generally released. + +## 2011-01-08 + +- Replaced the DExtra_Hosts.txt with the one from the ircDDB Gateway package. +- More changes to buffering and timing in the GMSK Repeater. +- Not generally released. +- 20110108a +- More changes to buffering and timing in the GMSK Repeater. +- Not generally released. + +## 2011-01-07 + +- Removed the statistics from the GMSK Repeater. +- Fixed the Linux compile bug from 20110105. +- Added a PTT maintainer for the GMSK Repeater. +- 20110107a +- A change to the PTT maintainer which may be more effective. +- Increased the buffer size for RF data. +- 20110107b +- Return to the PTT system of 20110107. +- Increase the buffer size for the RF data again, it is now 50 frames. +- Not generally released. + +## 2011-01-05 + +- Added the PTT Hang parameter to the D-Star Repeater. +- Added the XRF019, XRF026, XRF027, and XRF044 reflectors. +- Revert to using libusb. + +## 2010-12-12 + +- Test a new version of the libusb GMSK Repeater. +- Clean up the GUI a little for the Flags and BER. + +## 2010-12-11 + +- The returned BER was wrong, it should have been the number of error bits. +- The DExtra Gateway now reports the link status correctly to the repeaters. +- Added French, German, Danish, Italian and Polish to the DExtra Gateway ack languages. +- Added the XRF055 reflector. +- Back to using NAtools. + +## 2010-12-08 + +- Converted the GMSK Repeater to use libusb instead of NAtools. +- Changed to a new extension protocol to pass data to/from a gateway. +- Collect extra statistics and put the AMBE BER on the GUI. +- Added a statistics packet. + +## 2010-12-03 + +- Another stab at full-duplex on the GMSK Modem. +- Handle out-of-order data on the network traffic. +- 20101203a +- Changed to wxWidgets 2.8.11. +- Fixed half/full duplex modem setting. + +## 2010-12-02 + +- Fix the statistics for all the repeaters, hopefully. +- Changed the callsigns in the acks to be Icom compatible. +- Added more intelligent silence insertion. + +## 2010-11-21 + +- Added the Active output for the URI USB at the expense of GUI Output 2. +- Changed the end of transmission network packet to be more standard. +- More changes to the GMSK Repeater. + +## 2010-11-13 + +- Changed the timing within the GMSK Repeater. +- Fix a problem with receiving text from the latest ircDDB Gateway. + +## 2010-11-12 + +- Logs are now written into the /var/log directory under Linux. +- Removed the writer thread and gone back to single threading. +- Added the Intel.o file for i386 to allow the GMSK Repeater to build. + +## 2010-11-10 + +- Another try, but still not working properly + +## 2010-11-09 + +- More changes to the GMSK Repeater. +- 20101109a +- Plan B for getting full duplex working with the GMSK Repeater, a seperate thread for the transmitter. + +## 2010-11-07 + +- More changes to the GMSK Repeater. +- Add the -gui command line flag to all the programs. + +## 2010-11-04 + +- Changes to the GMSK Repeater. +- Pass Flag2 and Flag2 through the D-Star Repeater. +- The GMSK Repeater can now build on an AMD64 Linux machine. + +## 2010-10-26 + +- Changed the way the header is received in the GMSK Repeater. +- Increased the watchdog timer to two seconds from half second. + +## 2010-10-16 + +- Allow use of shared libraries on Linux. +- Fixed header transmission for the GMSK Repeater. +- Reject relayed gateway-ed data to stop confusing G2. + +## 2010-09-24 + +- Added display of the ack text to the repeater GUIs. +- Use Fred PA4YBR's modem support library for GMSK Modems. + +## 2010-09-22 + +- More callsign changes in the repeaters and Parrot Controller. +- Changes timings for the GMSK Modem. +- Not generally released. + +## 2010-09-17 + +- More callsign changes. +- More work on the GMSK Modem support. +- Not generally released. + +## 2010-09-15 + +- Allow for the choice of the GMSK modem type, Satoshi or Dutch*Star. Only the +- Satoshi option works as expected. +- Added text from the DExtra Reflector to appear in the repeater ack and beacon. +- More changes for the GMSK Modem, it appears even better now. +- A number of changes to the transmitted callsigns made. +- Not generally released. + +## 2010-09-07 + +- The GMSK Repeater now works on a Satoshi 7.08 system. +- Not generally released. + +## 2010-09-03 + +- Modify the slow data when in gateway mode. +- Not generally released. + +## 2010-09-01 + +- Added optional callsign restriction to both D-Star repeaters. +- Added repeater callsign to the DExtra Gateway configuration, as this fixed a +- bug. +- Not generally relased. + +## 2010-08-26 + +- Linux support for the DExtra Gateway. +- Written documentation for the DExtra Gateway. + +## 2010-08-25 + +- First working version of the DExtra Gateway. +- Not generally released. +- 20100825a +- Bug fixes to the DExtra Gateway. +- Not generally released. + +## 2010-08-23 + +- Added the CTCSS hang timer. +- Not generally released. + +## 2010-08-20 + +- Added the DExtra Gateway. +- Added hourly usage statistics to all of the repeaters. +- Not generally released. + +## 2010-08-18 + +- Added the GMSK repeater. +- Not generally released. + +## 2010-08-15 + +- Re-integrated the configuration for both repeaters back into the main +- repeater programs. +- Fix startup and shutdown when PTT inversion is set. +- Not generally released. + +## 2010-08-13 + +- Added a beacon to the D-Star repeater. +- Fixed bug in the new slow data decoder. + +## 2010-08-11 + +- Improvements to the slow data decoder. + +## 2010-08-10 + +- Added 79.7 Hz as a CTCSS tone. +- Added an RX Level setting to the D-Star repeater for when running alongside +- analogue for adjusting input levels. +- Bug in the slow data decoder fixed. +- Not generally released. + +## 2010-08-08 + +- Fix to the DVTool files. +- Fixed shutdown command. +- PortAudio parameter depends on the operating system, new parameter for Linux, +- old for Windows. + +## 2010-07-26 + +- Added new CTCSS output modes. +- Changed the parameter of PortAudio which appears to make a big difference under +- Linux. +- Not generally released. + +## 2010-07-20 + +- More bug fixes. +- CTCSS goes off before sending the callsign in lockout. + +## 2010-07-19 + +- A bug fix on the previous release. + +## 2010-07-12 + +- Better handling of error conditions at startup on the repeaters. +- Added new time to limit the timeout tones on the analogue repeater. +- Change sound card handling times. +- Not generally released. + +## 2010-06-10 + +- Added the end of transmission senc at the end of the network transmision. +- Lengthened the D-Star transmit period. +- Not generally released. + +## 2010-06-04 + +- Bug fixes. +- Re-implement the audio delay as negative PTT delay. +- Not generally released. + +## 2010-06-03 + +- Bug fixes. +- Use the ack timer to pause the D-Star repeater in gateway mode between overs. +- Added an optional audio delay to the D-Star output audio. +- Not generally released. + +## 2010-05-16 + +- Extra D-Star header validation. + +## 2010-05-10 + +- A bit of a stinker, best forgotten. +- Not generally released. + +## 2010-04-24 + +- Threading bug in 20100423 fixed. +- Not generally released. + +## 2010-04-23 + +- Moved output controls to the menu bar instead of buttons. +- Display the last three lines of the log on the repeater screen. +- Not generally released. + +## 2010-04-19 + +- Special test release for GB3IN. +- Not generally released. + +## 2010-04-15 + +- Re-added the ack timer, but it can now handle shorter times than before. +- Allow for the manual setting of the D-Star gateway callsign. +- Fixed a major bug in 20100412. + +## 2010-04-12 + +- Changed ack selection to free form text in the analogue repeater. +- Removed the need for a squelch input or the audio delay setting for D-Star. +- Now takes the header from slow data if the header was missed. +- Removed the ack timer. +- Better handling of missing sync bytes on both the radio and network sides. + +## 2010-02-23 + +- Bug fixes for 20100222. + +## 2010-02-22 + +- Added a gateway callsign to the D-Star repeater for messages. +- Removed the Icom G2 Protocol Hander as it wasn't used. +- Allow for a -nolog command line option to disable all logging. +- Simplified the mode selection and added an ack option for D-Star. +- Change network validation to only be on the IP address. +- Not generally relased. + +## 2010-01-14 + +- Bug fixes for 20100112. + +## 2010-01-12 + +- Fixed Active output bug in 20100111 in the D-Star repeater. +- Added a Disable input to the URI USB. +- Allows both external commands for Startup and Shutdown and the Disable input. +- Not generally released. + +## 2010-01-11 + +- Added menu commands to run Command 1 and 2 on the analogue repeater. +- Removed the timed command added in 20100107. +- Added a Disable input to shutdown and startup the repeaters. +- Modified the Active output to use with the new Disable input to allow cross linking of repeaters leading to true dual-mode operation. +- Fixed bug with K8055s with addresses greater than zero. +- Not generally released. + +## 2010-01-08 + +- Fixed Linux compilation issues. + +## 2010-01-07 + +- Added a command to be run at a period after the D-Star Repeater closes. +- Not generally released. + +## 2010-01-06 + +- Added Watchdog timer to the Parrot Controller. +- Added suffix D for 10m to the D-Star Repeater. +- Added two external commands to both repeaters. +- Added the four outputs to the D-Star control to match the analogue Repeater. +- Not generally released. + +## 2009-12-31 + +- Fixed bugs in the Parrot Controller. +- Added adjustable Turnaround Time to the Parrot Controller configuration. + +## 2009-12-30 + +- First version of the Parrot Controller. +- Small change to the G2 protocols. +- Not generally released. + +## 2009-12-29 + +- Fixed Linux build issues with 20091228. +- Included incomplete Parrot Controller. + +## 2009-12-28 + +- Tweaks to the DTMF controller. + +## 2009-12-26 + +- Added Parrot mode to the D-Star repeater. +- Added DTMF display to the analogue repeater. +- Not generally released. + +## 2009-12-19 + +- Added DTMF and local commands to the analogue repeater. +- Added remote and local commands to the D-Star repeater. +- Added Callsign Holdoff to the analogue repeater. +- Removed the RPT2 validation that was added for the last release. +- Added new programs to configure the repeaters and removed configuration from the +- main programs. +- Added two new programs, rmk8055drv and rmuridrv, to remove the Linux usbhid +- driver. +- Fixed Simplex/Duplex choice bug. +- Not generally released. + +## 2009-12-12 + +- Added the Gateway mode to the D-Star repeater. +- Don't pass through RF data with the RPT2 entry set to my callsign. +- Brought the K8055 drivers internal for both Windows and Linux. +- Put the ack back in simplex mode. +- Added the callsign on latch to the analogue repeater. +- Added a choice of timeout tones to the analogue repeater. + +## 2009-11-26 + +- Bug fix release for network delay. +- Don't pass through RF data with the RPT2 entry set to blanks. +- Not generally released. + +## 2009-11-25 + +- Changes to the "Open G2" network protocol. +- Added the network delay for the D-Star repeater. +- Not generally released. + +## 2009-11-20 + +- Merged the callsign and beacon timers in the analogue repeater. +- Support the URI USB. + +## 2009-11-17 + +- Added support for serial ports as I/O ports for both repeaters. +- Removed the ack when in simplex mode. +- Added all the EIA CTCSS tones to the analogue repeater. +- Changes to the "Open G2" network protocol. + +## 2009-11-06 + +- A cleanup of the 20091104 release because it works so well and deserves to be +- released :-) + +## 2009-11-04 + +- Changes to the protocol handlers to handle odd cases. +- Not generally released. + +## 2009-11-03 + +- Move the log files to the users home directory. + +## 2009-10-31 + +- More changes to the "Open G2" network protocol. +- Allow network data relaying while waiting for the ack. +- Not generally released. + +## 2009-10-30 + +- More changes to the "Open G2" network protocol. +- Not generally released. + +## 2009-10-29 + +- Implemented the "Open G2" network protocol. +- Not generally released. + +## 2009-10-27 + +- Removed RPT2 validation from network received headers. +- More G2 changes. +- Not generally released. + +## 2009-10-26 + +- Added a new bandpass filter to the analogue repeater to remove the CTCSS tones. +- More G2 changes. + +## 2009-10-25 + +- Changed to the GMSK modulator to be more conventional. +- Added command line versions of the repeaters. +- Major updates to the G2 protocol. +- Not generally released. + +## 2009-10-21 + +- Removed the confirmation prompt when exiting the repeaters. +- Not generally released. + +## 2009-10-20 + +- Lots of Linux GUI cleanups. +- Added the Watchdog timer to the D-Star GUI. +- Added a simplex mode to the D-Star repeater. +- Added an output level to the D-Star repeater to allow for sharing with the +- analogue controller. +- Not generally released. + +## 2009-10-17 + +- Allow for the configuration of the local IP address to bind to. +- Not generally released. + +## 2009-10-16 + +- Added a network watchdog timer to handle the loss of the end packet. + +## 2009-10-15 + +- Allow for named configurations to allow multiple repeaters on one machine. +- Increased the maximum PTT Delay to 500ms from 200ms. +- Added the PTT Delay and PTT and Squelch inversion to the Analogue Repeater. +- More changes to the G2 protocol handler. +- Not generally released. + +## 2009-10-08 + +- Changes in the G2 protocol handler. +- Not generally released. + +## 2009-10-02 + +- Removed RPT2 validation. +- Added PTT and Squelch inversion for the D-Star Repeater. +- Added G2 Gateway support. +- Tightened the filter before the GMSK democulator. +- Not generally released. + +## 2009-09-23 + +- Validate the RPT2 callsign. +- Fixed bit errors on the D-Star Ack. +- Improved the documentation about the D-Star Repeater. +- Added a PTT delay for D-Star. + +## 2009-09-17 + +- Linux cleanups. +- Changes to the D-Star Controller. +- Not generally released. + +## 2009-09-16 + +- The External Transmit is only set when relaying radio audio. +- First version of the D-Star Controller. +- Optimised the K8055 communication. + +## 2009-09-11 + +- Add a battery input and a change of Ack to signify battery operation. +- Not generally released. + +## 2009-09-10 + +- Allow for an external CTCSS decoder. +- Not generally released. + +## 2009-09-09 + +- Turn off the CTCSS early when the end callsign is enabled because of slow +- responding decoders in radios allowing some of the callsign through. +- Added an optional audio delay on the input to synchronise with the squelch +- input(s). +- Make GUI updates optional. + +## 2009-08-24 + +- Audio levels are now reset before each received transmission instead of at the +- Ack. +- Added controls to toggle the unused output lines to control external hardware. +- Delay renamed to Ack in the analogue repeater GUI. +- Fixed bugs in the state machine causing timeout resets and a couple of missing +- state transitions. + +## 2009-08-13 + +- Fixed audio out bug. +- CTCSS output is now for the whole time the repeater is accessed. + +## 2009-08-12 + +- Fixed CTCSS output tones. +- Widened the CTCSS detector from 2Hz to 5Hz and from 500ms to 200ms. +- Numerous internal improvements. +- Not generally released. + +## 2009-06-30 + +- Changed the main filter back to the FIR for performance reasons. +- First version to be used on a real repeater, GB3IN on 2m. + +## 2009-06-29 + +- Switched off filters when not transmitting to reduce CPU load. +- Moved CTCSS insertion after pre-emphasis filter. +- Not generally released. + +## 2009-06-26 + +- Converted the pre-emphasis, de-emphasis and audio filters to IIRs for efficiency +- and better shapes. +- Show VOGAD levels for the Radio and External on the main screen. +- Not generally released. + +## 2009-06-19 + +- Added a VOGAD unit, and made the VOGAD on both the Radio and External audio. +- Not generally released. + +## 2009-06-18 + +- Removed the icons from the programs. +- Increased the audio output for the external output. +- Not generally released. + +## 2009-06-17 + +- The D-Star Repeater now compiles cleanly on Windows and Linux, so a good time to +- do a release. + +## 2009-06-16 + +- Added the D-Star Repeater, but unfinished. +- Optimised access to the K8055. +- Not generally released. + +## 2009-06-13 + +- Added Linux support, but not tested. +- Changed the name of the executable and directories. +- Changed how the K8055 parameters are set. +- Allow for COM ports higher than COM10. +- Not generally released. + +## 2009-06-10 + +- Added the audio level value to the GUI. +- Re-arranged the GUI layout. +- Not generally released. + +## 2009-06-09 + +- Added a simple VOGAD with reporting in the GUI. +- Reworked the filters and output levels. +- Not generally released. + +## 2009-06-08 + +- Added debugging information in the tone decoder. +- Not generally released. + +## 2009-06-07 + +- Silly bug in the through audio fixed. +- Added the option to use a sound file as an input, but only in a debug build. + +## 2009-05-30 + +- Added audio filtering and optional pre- and de-emphasis. Also modified the +- configuration to have less pages as well as add the new settings. diff --git a/CHANGES.txt b/CHANGES.txt deleted file mode 100644 index 8aee807..0000000 --- a/CHANGES.txt +++ /dev/null @@ -1,2098 +0,0 @@ -Repeater - 20180710 -=================== - -20090530 --------- - -Added audio filtering and optional pre- and de-emphasis. Also modified the -configuration to have less pages as well as add the new settings. - -20090607 --------- - -Silly bug in the through audio fixed. -Added the option to use a sound file as an input, but only in a debug build. - -20090608 --------- - -Added debugging information in the tone decoder. -Not generally released. - -20090609 --------- - -Added a simple VOGAD with reporting in the GUI. -Reworked the filters and output levels. -Not generally released. - -20090610 --------- - -Added the audio level value to the GUI. -Re-arranged the GUI layout. -Not generally released. - -20090613 --------- -Added Linux support, but not tested. -Changed the name of the executable and directories. -Changed how the K8055 parameters are set. -Allow for COM ports higher than COM10. -Not generally released. - -20090616 --------- - -Added the D-Star Repeater, but unfinished. -Optimised access to the K8055. -Not generally released. - -20090617 --------- - -The D-Star Repeater now compiles cleanly on Windows and Linux, so a good time to -do a release. - -20090618 --------- - -Removed the icons from the programs. -Increased the audio output for the external output. -Not generally released. - -20090619 --------- - -Added a VOGAD unit, and made the VOGAD on both the Radio and External audio. -Not generally released. - -20090626 --------- - -Converted the pre-emphasis, de-emphasis and audio filters to IIRs for efficiency -and better shapes. -Show VOGAD levels for the Radio and External on the main screen. -Not generally released. - -20090629 --------- - -Switched off filters when not transmitting to reduce CPU load. -Moved CTCSS insertion after pre-emphasis filter. -Not generally released. - -200900630 ---------- - -Changed the main filter back to the FIR for performance reasons. -First version to be used on a real repeater, GB3IN on 2m. - -20090812 --------- - -Fixed CTCSS output tones. -Widened the CTCSS detector from 2Hz to 5Hz and from 500ms to 200ms. -Numerous internal improvements. -Not generally released. - -20090813 --------- - -Fixed audio out bug. -CTCSS output is now for the whole time the repeater is accessed. - -20090824 --------- - -Audio levels are now reset before each received transmission instead of at the -Ack. -Added controls to toggle the unused output lines to control external hardware. -Delay renamed to Ack in the analogue repeater GUI. -Fixed bugs in the state machine causing timeout resets and a couple of missing -state transitions. - -20090909 --------- - -Turn off the CTCSS early when the end callsign is enabled because of slow -responding decoders in radios allowing some of the callsign through. -Added an optional audio delay on the input to synchronise with the squelch -input(s). -Make GUI updates optional. - -20090910 --------- - -Allow for an external CTCSS decoder. -Not generally released. - -20090911 --------- - -Add a battery input and a change of Ack to signify battery operation. -Not generally released. - -20090916 --------- - -The External Transmit is only set when relaying radio audio. -First version of the D-Star Controller. -Optimised the K8055 communication. - -20090917 --------- - -Linux cleanups. -Changes to the D-Star Controller. -Not generally released. - -20090923 --------- - -Validate the RPT2 callsign. -Fixed bit errors on the D-Star Ack. -Improved the documentation about the D-Star Repeater. -Added a PTT delay for D-Star. - -20091002 --------- - -Removed RPT2 validation. -Added PTT and Squelch inversion for the D-Star Repeater. -Added G2 Gateway support. -Tightened the filter before the GMSK democulator. -Not generally released. - -20091008 --------- - -Changes in the G2 protocol handler. -Not generally released. - -20091015 --------- - -Allow for named configurations to allow multiple repeaters on one machine. -Increased the maximum PTT Delay to 500ms from 200ms. -Added the PTT Delay and PTT and Squelch inversion to the Analogue Repeater. -More changes to the G2 protocol handler. -Not generally released. - -20091016 --------- - -Added a network watchdog timer to handle the loss of the end packet. - -20091017 --------- - -Allow for the configuration of the local IP address to bind to. -Not generally released. - -20091020 --------- - -Lots of Linux GUI cleanups. -Added the Watchdog timer to the D-Star GUI. -Added a simplex mode to the D-Star repeater. -Added an output level to the D-Star repeater to allow for sharing with the -analogue controller. -Not generally released. - -20091021 --------- - -Removed the confirmation prompt when exiting the repeaters. -Not generally released. - -20091025 --------- - -Changed to the GMSK modulator to be more conventional. -Added command line versions of the repeaters. -Major updates to the G2 protocol. -Not generally released. - -20091026 --------- - -Added a new bandpass filter to the analogue repeater to remove the CTCSS tones. -More G2 changes. - -20091027 --------- - -Removed RPT2 validation from network received headers. -More G2 changes. -Not generally released. - -20091029 --------- - -Implemented the "Open G2" network protocol. -Not generally released. - -20091030 --------- - -More changes to the "Open G2" network protocol. -Not generally released. - -20091031 --------- - -More changes to the "Open G2" network protocol. -Allow network data relaying while waiting for the ack. -Not generally released. - -20091103 --------- - -Move the log files to the users home directory. - -20091104 --------- - -Changes to the protocol handlers to handle odd cases. -Not generally released. - -20091106 --------- - -A cleanup of the 20091104 release because it works so well and deserves to be -released :-) - -20091117 --------- - -Added support for serial ports as I/O ports for both repeaters. -Removed the ack when in simplex mode. -Added all the EIA CTCSS tones to the analogue repeater. -Changes to the "Open G2" network protocol. - -20091120 --------- - -Merged the callsign and beacon timers in the analogue repeater. -Support the URI USB. - -20091125 --------- - -Changes to the "Open G2" network protocol. -Added the network delay for the D-Star repeater. -Not generally released. - -20091126 --------- - -Bug fix release for network delay. -Don't pass through RF data with the RPT2 entry set to blanks. -Not generally released. - -20091212 --------- - -Added the Gateway mode to the D-Star repeater. -Don't pass through RF data with the RPT2 entry set to my callsign. -Brought the K8055 drivers internal for both Windows and Linux. -Put the ack back in simplex mode. -Added the callsign on latch to the analogue repeater. -Added a choice of timeout tones to the analogue repeater. - -20091219 --------- - -Added DTMF and local commands to the analogue repeater. -Added remote and local commands to the D-Star repeater. -Added Callsign Holdoff to the analogue repeater. -Removed the RPT2 validation that was added for the last release. -Added new programs to configure the repeaters and removed configuration from the -main programs. -Added two new programs, rmk8055drv and rmuridrv, to remove the Linux usbhid -driver. -Fixed Simplex/Duplex choice bug. -Not generally released. - -20091226 --------- - -Added Parrot mode to the D-Star repeater. -Added DTMF display to the analogue repeater. -Not generally released. - -20091228 --------- - -Tweaks to the DTMF controller. - -20091229 --------- - -Fixed Linux build issues with 20091228. -Included incomplete Parrot Controller. - -20091230 --------- - -First version of the Parrot Controller. -Small change to the G2 protocols. -Not generally released. - -20091231 --------- - -Fixed bugs in the Parrot Controller. -Added adjustable Turnaround Time to the Parrot Controller configuration. - -20100106 --------- - -Added Watchdog timer to the Parrot Controller. -Added suffix D for 10m to the D-Star Repeater. -Added two external commands to both repeaters. -Added the four outputs to the D-Star control to match the analogue Repeater. -Not generally released. - -20100107 --------- - -Added a command to be run at a period after the D-Star Repeater closes. -Not generally released. - -20100108 --------- - -Fixed Linux compilation issues. - -20100111 --------- - -Added menu commands to run Command 1 and 2 on the analogue repeater. -Removed the timed command added in 20100107. -Added a Disable input to shutdown and startup the repeaters. -Modified the Active output to use with the new Disable input to allow cross linking of repeaters leading to true dual-mode operation. -Fixed bug with K8055s with addresses greater than zero. -Not generally released. - -20100112 --------- - -Fixed Active output bug in 20100111 in the D-Star repeater. -Added a Disable input to the URI USB. -Allows both external commands for Startup and Shutdown and the Disable input. -Not generally released. - -20100114 --------- - -Bug fixes for 20100112. - -20100222 --------- - -Added a gateway callsign to the D-Star repeater for messages. -Removed the Icom G2 Protocol Hander as it wasn't used. -Allow for a -nolog command line option to disable all logging. -Simplified the mode selection and added an ack option for D-Star. -Change network validation to only be on the IP address. -Not generally relased. - -20100223 --------- - -Bug fixes for 20100222. - -20100412 --------- - -Changed ack selection to free form text in the analogue repeater. -Removed the need for a squelch input or the audio delay setting for D-Star. -Now takes the header from slow data if the header was missed. -Removed the ack timer. -Better handling of missing sync bytes on both the radio and network sides. - -20100415 --------- - -Re-added the ack timer, but it can now handle shorter times than before. -Allow for the manual setting of the D-Star gateway callsign. -Fixed a major bug in 20100412. - -20100419 --------- - -Special test release for GB3IN. -Not generally released. - -20100423 --------- - -Moved output controls to the menu bar instead of buttons. -Display the last three lines of the log on the repeater screen. -Not generally released. - -20100424 --------- - -Threading bug in 20100423 fixed. -Not generally released. - -20100510 --------- - -A bit of a stinker, best forgotten. -Not generally released. - -20100516 --------- - -Extra D-Star header validation. - -20100603 --------- - -Bug fixes. -Use the ack timer to pause the D-Star repeater in gateway mode between overs. -Added an optional audio delay to the D-Star output audio. -Not generally released. - -20100604 --------- - -Bug fixes. -Re-implement the audio delay as negative PTT delay. -Not generally released. - -20100610 --------- - -Added the end of transmission senc at the end of the network transmision. -Lengthened the D-Star transmit period. -Not generally released. - -20100712 --------- - -Better handling of error conditions at startup on the repeaters. -Added new time to limit the timeout tones on the analogue repeater. -Change sound card handling times. -Not generally released. - -20100719 --------- - -A bug fix on the previous release. - -20100720 --------- - -More bug fixes. -CTCSS goes off before sending the callsign in lockout. - -20100726 --------- - -Added new CTCSS output modes. -Changed the parameter of PortAudio which appears to make a big difference under -Linux. -Not generally released. - -20100808 --------- - -Fix to the DVTool files. -Fixed shutdown command. -PortAudio parameter depends on the operating system, new parameter for Linux, -old for Windows. - -20100810 --------- - -Added 79.7 Hz as a CTCSS tone. -Added an RX Level setting to the D-Star repeater for when running alongside -analogue for adjusting input levels. -Bug in the slow data decoder fixed. -Not generally released. - -20100811 --------- - -Improvements to the slow data decoder. - -20100813 --------- - -Added a beacon to the D-Star repeater. -Fixed bug in the new slow data decoder. - -20100815 --------- - -Re-integrated the configuration for both repeaters back into the main -repeater programs. -Fix startup and shutdown when PTT inversion is set. -Not generally released. - -20100818 --------- - -Added the GMSK repeater. -Not generally released. - -20100820 --------- - -Added the DExtra Gateway. -Added hourly usage statistics to all of the repeaters. -Not generally released. - -20100823 --------- - -Added the CTCSS hang timer. -Not generally released. - -20100825 --------- - -First working version of the DExtra Gateway. -Not generally released. - -20100825a ---------- - -Bug fixes to the DExtra Gateway. -Not generally released. - -20100826 --------- - -Linux support for the DExtra Gateway. -Written documentation for the DExtra Gateway. - -20100901 --------- - -Added optional callsign restriction to both D-Star repeaters. -Added repeater callsign to the DExtra Gateway configuration, as this fixed a -bug. -Not generally relased. - -20100903 --------- - -Modify the slow data when in gateway mode. -Not generally released. - -20100907 --------- - -The GMSK Repeater now works on a Satoshi 7.08 system. -Not generally released. - -20100915 --------- - -Allow for the choice of the GMSK modem type, Satoshi or Dutch*Star. Only the -Satoshi option works as expected. -Added text from the DExtra Reflector to appear in the repeater ack and beacon. -More changes for the GMSK Modem, it appears even better now. -A number of changes to the transmitted callsigns made. -Not generally released. - -20100917 --------- - -More callsign changes. -More work on the GMSK Modem support. -Not generally released. - -20100922 --------- - -More callsign changes in the repeaters and Parrot Controller. -Changes timings for the GMSK Modem. -Not generally released. - -20100924 --------- - -Added display of the ack text to the repeater GUIs. -Use Fred PA4YBR's modem support library for GMSK Modems. - -20101016 --------- - -Allow use of shared libraries on Linux. -Fixed header transmission for the GMSK Repeater. -Reject relayed gateway-ed data to stop confusing G2. - -20101026 --------- - -Changed the way the header is received in the GMSK Repeater. -Increased the watchdog timer to two seconds from half second. - -20101104 --------- - -Changes to the GMSK Repeater. -Pass Flag2 and Flag2 through the D-Star Repeater. -The GMSK Repeater can now build on an AMD64 Linux machine. - -20101107 --------- - -More changes to the GMSK Repeater. -Add the -gui command line flag to all the programs. - -20101109 --------- - -More changes to the GMSK Repeater. - -20101109a ---------- - -Plan B for getting full duplex working with the GMSK Repeater, a seperate thread for the transmitter. - -20101110 --------- - -Another try, but still not working properly - -20101112 --------- - -Logs are now written into the /var/log directory under Linux. -Removed the writer thread and gone back to single threading. -Added the Intel.o file for i386 to allow the GMSK Repeater to build. - -20101113 --------- - -Changed the timing within the GMSK Repeater. -Fix a problem with receiving text from the latest ircDDB Gateway. - -20101121 --------- - -Added the Active output for the URI USB at the expense of GUI Output 2. -Changed the end of transmission network packet to be more standard. -More changes to the GMSK Repeater. - -20101202 --------- - -Fix the statistics for all the repeaters, hopefully. -Changed the callsigns in the acks to be Icom compatible. -Added more intelligent silence insertion. - -20101203 --------- - -Another stab at full-duplex on the GMSK Modem. -Handle out-of-order data on the network traffic. - -20101203a ---------- - -Changed to wxWidgets 2.8.11. -Fixed half/full duplex modem setting. - -20101208 --------- - -Converted the GMSK Repeater to use libusb instead of NAtools. -Changed to a new extension protocol to pass data to/from a gateway. -Collect extra statistics and put the AMBE BER on the GUI. -Added a statistics packet. - -20101211 --------- - -The returned BER was wrong, it should have been the number of error bits. -The DExtra Gateway now reports the link status correctly to the repeaters. -Added French, German, Danish, Italian and Polish to the DExtra Gateway ack languages. -Added the XRF055 reflector. -Back to using NAtools. - -20101212 --------- - -Test a new version of the libusb GMSK Repeater. -Clean up the GUI a little for the Flags and BER. - -20110105 --------- - -Added the PTT Hang parameter to the D-Star Repeater. -Added the XRF019, XRF026, XRF027, and XRF044 reflectors. -Revert to using libusb. - -20110107 --------- - -Removed the statistics from the GMSK Repeater. -Fixed the Linux compile bug from 20110105. -Added a PTT maintainer for the GMSK Repeater. - -20110107a ---------- - -A change to the PTT maintainer which may be more effective. -Increased the buffer size for RF data. - -20110107b ---------- - -Return to the PTT system of 20110107. -Increase the buffer size for the RF data again, it is now 50 frames. -Not generally released. - -20110108 --------- - -Replaced the DExtra_Hosts.txt with the one from the ircDDB Gateway package. -More changes to buffering and timing in the GMSK Repeater. -Not generally released. - -20110108a ---------- - -More changes to buffering and timing in the GMSK Repeater. -Not generally released. - -20110109 --------- - -Allow for the use of both the libUSB and NAtools interface. -Change how the internal timing operates. -Not generally released. - -20110109a ---------- - -Increase the buffer size and put in a cycle of silence to start it off. -Not generally released. - -20110114 --------- - -Swap RPT1 and RPT2 for the libUSB modem driver. -Different GMSK modem buffer filling routines. -Not generally released. - -20110114a ---------- - -Same as 20110114 but with additional debugging information. -Not generally released. - -20110116 --------- - -Swap RPT1 and RPT2 for the NAtools modem driver. -Increase the polling rate in the GMSK Repeater. -Not generally released. - -20110120 --------- - -Added XRF000 for the DExtra Gateway. -DExtra protocol has been upgraded. -Another timing change for the GMSK Repeater receiver. - -20110121 --------- - -More timing changes for the GMSK Repeater. - -20110123 --------- - -More timing changes for the GMSK Repeater. - -20110205 --------- - -Fixed the callsigns in the acks of the GMSK Repeater. -Change the timings, particularly when handling receive on the modem. -Change the default ports to match those of ircDDB Gateway. - -20110208 --------- - -More timing changes to the GMSK Repeater. - -20110210 --------- - -Handle errors in the GMSK modem better. -More timing changes to the GMSK Repeater. - -20110213 --------- - -Internal restructuring of the GMSK Repeater. -Removed the RX State from the GMSK Repeater GUI. - -20110215 --------- - -Added XRF020 to DExtra Gateway. -Added Dutch*Star 1.00.0 firmware compatibility again. - -20110217 --------- - -Fixed bug in DExtraGateway with incoming headers disrupting communications. -Change the maximum Parrot Controller beacon timer to 20 minutes. -More GMSK Repeater internal changes. - -20110219 --------- - -Swapped the callsigns in the D-Star Repeater to match the GMSK Repeater. -Removed support for Dutch*Star 1.00.0 firmware. - -20110219a ---------- - -Re-arranged the transmitted callsigns in both of the digital repeaters. -Validate the incoming network header for both digital repeaters. - -20110222 --------- - -Added Swedish and Spanish translations to the DExtra Gateway. -Increased the USB timeout when using libUSB with the GMSK modem. -Increased the packet silence timeout from 30ms to 40ms. -Run the network processing from the timer thread in the GMSK Repeater. - -20110222a ---------- - -Small change to the GMSK Repeater to stop lock-ups. - -20110227 --------- - -Added optional RPT1 validation. -Relaxed the timing when talking to the GMSK modem. - -20110301 --------- - -Remove a delay added to the NAtools driver. -Fixed Linux compile error with the previous release. - -20110308 --------- - -Added daily logs. -Added the -logdir command line option to the programs. -Added XRF007 and XRF021 to the DExtra_Hosts.txt file. -Added the sound card demodulator bias patch from Eric NS7DQ. - -20110308a ---------- - -Timing changes to the GMSK Repeater closer to the settings of 20110219a. - -20110327 --------- - -Removed the bias patch which was added to the 20110308 release. -Removed the silly GMSK Repeater error messages that I added. -Updated the IP address of XRF008. -Added extra my callsign validation to the repeaters. - -20110329 --------- - -Relax the header checksum testing under certain circumstances. -Check the incoming my call with a regular expression. -More buffering of network data in the GMSK Repeater. - -20110331 --------- - -Timing changes in the GMSK Repeater. -Put the networking back into the second thread on the GMSK Repeater. - -20110402 --------- - -Refinements to 20110331 based on feedback. - -20110403 --------- - -Mutex locking in the last couple of releases was a little wrong. - -20110404 --------- - -More mutex locking changes in the GMSK Repeater. -Timing changes in the GMSK Repeater. - -20110411 --------- - -Different method of silence insertion used. - -20110413 --------- - -A small tidy up of the previous release. - -20110417 --------- - -Changed from wxWidgets 2.8.11 to 2.8.12. -Small internal timing tweak for the GMSK Repeater. - -20110418 --------- - -Added XRF033. -Updated the IP address of XRF013. -Changed the timing in the main loop. -Not generally released. - -20110420 --------- - -Changed the timing of feeding the data to the GMSK Modem. - -20110421 --------- - -Fixed an obscure bug in the Parrot Controller. -Updated the IP address of XRF021. -Change timings and data feed to the GMSK Modem. - -20110506 --------- - -Added XRF031. -Added the ack time to the minimum time for the analogue repeater controller. -Allow outgoing DExtra links to have random ports. -Change timings in the GMSK Repeater. - -20110508 --------- - -Added -daemon option to create proper daemons on Linux. -Not generally released. - -20110510 --------- - -Fundamental change to the GMSK Modem handling. -Cleaned up 20110508. - -20110512 --------- - -Updated the USAGE.txt file to include the -daemon option. -The pid of the programs is written to /var/run for Linux versions. -Added the -confdir option to the daemon versions of the programs. - -20110525 --------- - -Updated the IP address of XRF020. -Changed poll format to report the repeater type to the gateway. -Small change in timing of the GMSK Repeater. - -20110529 --------- - -Tweaks to the software type/version reporting to the gateway. - -20110529a ---------- - -Small GMSK modem change, rolls back a change made in 20110529. - -20110601 --------- - -Small GMSK modem change, rolls back another change made in 20110529. - -20110609 --------- - -Add the cycle time setting to the GMSK modem settings. -Added different serial port configs to allow for different serial port settings. - -20110610 --------- - -Added more cycle times for the GMSK modem. -Added an extra serial port config. - -20110613 --------- - -Display cycle overruns more intelligently. -Display time taken for the networking to complete. -Stop polling the URI USB in the D-Star Repeater, it's not needed. - -20110621 --------- - -Increased the max cycle time value to 20ms. -Now write 12 bytes per cycle instead of 8. - -20110621a ---------- - -Changed from the broken node_putdta to node_io. - -20110622 --------- - -Clean ups and removal of some logging messages. -Added extra delays for libUSB. - -20110628 --------- - -Remove the need to have NAtools installed on Windows. - -20110702 --------- - -Remove the need to have LibUSB installed on Windows. - -20110703 --------- - -Trying to handle unreasonable combinations of modem and USB chipset. - -20110704 --------- - -Allow for compilation without NAtools being available. -Added XRF028. - -20110706 --------- - -Changed timings when dealing with LibUSB and relaying from the modem. -Changed Modem Type to Firmware Type. - -20110708 --------- - -Upgraded the LibUSB driver from libusb-0.1 to libusb-1.0 under Linux. -Upgraded the NAtools version under Windows. -Removed NAtools on the Linux side. - -20110710 --------- - -Turned the logging upside down to match the gateway, and common sense. -Improvements to the NAtools drivers. - -20110711 --------- - -Handle DUTCH*Star 0.1.00 firmware explicitely. - -20110712 --------- - -Improve the clock accuracy in the GMSK Repeater. - -20110714 --------- - -Insert a 100ms delay before sending the data after sending the header. -Updated XRF033. - -20110716 --------- - -Added Nederlands as a language option to the DExtra Gateway. -Removed the cycle time option for the GMSK Repeater. -First version that is stable with DUTCH*Star 0.1.00 firmware. - -20110717 --------- - -Cleans-ups in all the repeaters. -More work on 0.1.00 stability. - -20110719 --------- - -Removed the Network Delay from the D-Star Repeater. -Added extra delay and buffering to the D-Star Repeater for network traffic. - -20110720 --------- - -Fix PTT bug in 20110719. - -20110808 --------- - -First version of the DVAP Node program. -Allow incoming network data during wait periods. -Clean up GMSK Repeater local repeating. - -20110808a ---------- - -Serial port selection bug fixed. - -20110808b ---------- - -Windows serial port changes. - -20110811 --------- - -Minor internal changes to all of the repeaters. -Improved timing for the DExtra Gateway and Parrot Controller. -Added fuzzy matching for the frame sync to the D-Star Repeater. - -20110811a ---------- - -Fix for GMSK Repeater full duplex mode. -Identify the OS and wxWidgets version in the log for all programs. - -20110814 --------- - -Added the noise squelch to the D-Star Repeater. -More work on GMSK full duplex mode. - -20110816 --------- - -Allow the timers to handle long timeouts correctly. -The Squelch Level affects the D-Star Repeater in real-time. -Change the serial port handling for the Analogue and D-Star repeaters. - -20110818 --------- - -The RX and TX Levels affect the D-Star Repeater in real-time. -The levels and thresholds in the Analogue Repeater operate in real-time. -Increased the Analogue Repeater hang timer maximum to 30 seconds. -All of the IDs and Acks in the Analogue Repeater can now be WAV files. - -20110822 --------- - -The Battery Ack is now configurable in the Analogue Repeater. -Fix to the Windows SerialLineController.cpp -Add more buffering to the D-Star Repeater. - -20110823 --------- - -Fixed a bug in the DExtra Gateway. -Allow the opening callsign of the Analogue Repeater to be different from the closing callsign. -Fixed the minimum time for ack timer in the Analogue Repeater. -Added XRF073. - -20110825 --------- - -Added a noise level display to the D-Star Repeater. -Added the Squelch Mode to the D-Star Repeater. - -20110826 --------- - -Allow for alternative GMSK Modem addresses in NAtools. -Use the new silence insertion in the D-Star Repeater. - -20110830 --------- - -Fix Squelch and Noise display when transmitting in the D-Star Repeater. -First untested version of the DV-RPTR Repeater. - -20110831 --------- - -More work on the DV-RPTR Repeater. - -20110902 --------- - -Seperate the closdown ID from the periodic ID in the Analogue Repeater. -Lots of internal changes to the DVAP Node. -More work on the DV-RPTR Repeater. - -20110905 --------- - -Considerable internal changes to the output queueing. -Bug in the silence inseration fixed. - -20110907 --------- - -More internal changes to handle extreme situations. - -20110913 --------- - -Use the official FTDI interface to access the DVAP on Windows. -Changed XRF002 IP address. - -20110915 --------- - -Revert the FTDI change for the DVAP on Windows. -Changes to the queueing. -Enable the debug flag on Linux builds. -More changes to the DV-RPTR Repeater. - -20110923 --------- - -More changes to the DV-RPTR Repeater. -Changed XRF073 IP address. - -20111004 --------- - -Changed how data is fed to the DVAP and DV-RPTR. -Incorporated the new DV-RPTR data formats. - -20111016 --------- - -Added XRF069. -Reverted external input handling for the Analogue Repeater. - -20111022 --------- - -Changes to the DV-RPTR protocol. -Added packet logging for local traffic for all DV repeaters. -Pass the headers from the DV repeaters through properly. -Increase the size and number of the network queues. -Changed XRF069 IP address. -Changes to the repeater to gateway protocol. - -20111102 --------- - -More changes to the DV-RPTR protocol. -The default log directory is now the home directory under Linux. -Bug fix to the DExtra Gateway. -Increased network security for the repeaters. -Added XRF123. - -20111109 --------- - -Output buffer changes. -Added support for the reporting of the two status messages. - -20111112 --------- - -Fix to the DExtra Gateway. -Minor bug fix. - -20111118 --------- - -Increased the GUI width on Linux to accomodate the new status messages. -Allow for the DV-RPTR TX Delay up to 500ms from 250ms. -Fixed the config file name for the DV-RPTR daemon version. -Fixed the DV-RPTR communications protocol. -Fixed DV-RPTR version string. -Updated XRF069 IP address. -Added XRF038. - -20111120 --------- - -Changes to the DVAP Node and DV-RPTR Repeater handling. - -20111122 --------- - -Allow more choices for the analogue repeater Ack delay. -Cleaned up the DV-RPTR Modem settings. -Updated the Windows NATools DLL. -Updated XRF002 IP address. -DExtra Gateway fix. - -20111125 --------- - -Handle startup of the DV-RPTR modem and DVAP better. - -20111126 --------- - -Changes to the DV-RPTR modem startup for slow serial subsystem starts. -Dummy gateway handling added. - -20111204 --------- - -Allow headers received when in full duplex and transmitting network data to be passed to the gateway. - -20111207 --------- - -Acks changed from the link status to the transmission time and BER. -Allow up to five status messages. - -20111208 --------- - -Fix bug in the D-Star Repeater for the new status messages. -Similar fix to the DVAP Node. -Small change to the new ack format. - -20111209 --------- - -Change the ack text to include the reflector instead of the time. -Updated XRF019s IP address. - -20111212 --------- - -In the Analogue Repeater send radio audio to the external port when relaying from the external port. -Removal of the USAGE.txt file. -Fix an ancient bug with the output switching in the D-Star Repeater. - -20111213 --------- - -More work on the Analogue Repeater radio audio to the external port. - -20111215 --------- - -More work on the Analogue Repeater radio audio to the external port. - -20111220 --------- - -More work on the Analogue Repeater radio audio to the external port. -Updated XRF031s IP address. - -20111221 --------- - -More work on the Analogue Repeater radio audio to the external port. - -20111222 --------- - -Allow the ack on the D-Star repeaters to be selectable for BER or status. - -20111231 --------- - -Added the upgraded Dummy Repeater to the repeater package. -Allow channel D in the DExtra Gateway. - -20120111 --------- - -Increase maximum D-Star Repeater hang time to 3000ms from 2000ms. -Improve gateway mode rejection rules. -Allow the Dummy Repeater to be controlled from external hardware. -Added XRF119. - -20120116 --------- - -Updated XRF033s IP addeess. -Fixed GUI PTT bug in the Dummy Repeater. -Add an audio resampler to the Dummy Repeater. - -20120125 --------- - -Allow for the settings of the beacon text for the D-Star repeaters. -Equalised the audio levels in the Dummy Repeater; -Updated XRF019 IPs address. -Added XRF110. - -20120205 --------- - -New configuration system for non-Windows platforms. -Added XRF040. - -20120206 --------- - -Bug fixes for 20120205. - -20120210 --------- - -Updated XRF021s IP address. -Small cleanups. - -20120213 --------- - -Fixed D-Star Repeater Windows configuration bug. - -20120221 --------- - -Changed the lower frequency limits of the analogue repeater callsign and ack. -Added the E (Echo) and I (Info) commands to the DExtra Gateway. -Updated XRF069s IP address. - -20120223 --------- - -Blank any DTMF tones from being transmitted over RF. -Added audio delay and pre-, de-emphasis to the Analogue Repeater external port. - -20120228 --------- - -Restore GUI window position on startup. -New repeater to gateway protocol implemented. -Allow background audio data to be transmitted. -Added a receive hang time to the Dummy Repeater. - -20120303 --------- - -Added AMBE regeneration to background data. -Added receive-only and transmit-only modes to the repeaters. - -20120308 --------- - -Changes to the DV-RPTR driver. -Updated XRF021s and XRF044s IP address. -Added XRF222, XRF353, and XRF777. -Not generally released. - -20120309 --------- - -Changes to support the DCS reflector system. -More changes to the DV-RPTR driver. - -20120310 --------- - -Updated the DV-RPTR driver again. -Binary only release for Windows users. - -20120310a ---------- - -Updated the DV-RPTR and DVAP drivers. - -20120312 --------- - -Added CM119A product id for latest URI USB hardware. -Added K8055 support to the GMSK and DV-RPTR repeaters. - -20120320 --------- - -Updated XRF021s IP address. -Added TX output to the K8055 with the GMSK and DV-RPTR repeaters. -Added the DCS Gateway. - -20120322 --------- - -Added XRF030. -Added the Split Repeater. -Allow minimum ack time in the Analogue Repeater to be 100, 300, and 500ms. -Add up to 100ms of squelch delay in the Analogue Repeater. - -20120324 --------- - -Added "TX and RX" mode to all of the D-Star repeaters except the DVAP Node. -More changes to the Split Repeater. -Added DCS009. - -20120326 --------- - -Fixed bugs in the RF D-Star repeaters when used with the Split Repeater. -Fixed bugs in the use of the Split Repeater with the RF repeaters. -Added DCS005. - -20120327 --------- - -Add extra logging to the Split Repeater for unknown IP addresses and ports. -Modify the Linux configuration system to fix a bug. -Updated XRF017s IP address. -Added DCS008. - -20120328 --------- - -Fixed ack bug in the Split Repeater. -Added DCS006. - -20120330 --------- - -Updated the DCS and DExtra hosts files to the latest versions. -Convert the main log files to use UTC instead of local time. -More changes to the Split Repeater. - -20120402 --------- - -Better error handling of the DV-Dongle. -More changes to the Split Repeater. - -20120403 --------- - -Sort the callsign entries in the Dummy Repeater. -Minor cleanups. - -20120411 --------- - -Added Norwegian as a language to the DExtra and DCS Gateway. -Added an optional voice beacon to all of the D-Star repeaters. - -20120412 --------- - -Fixed BER display on all of the repeater GUIs. -Fixed header transmission bug in diversity modes. -More changes to the Split Repeater. -Not generally released. - -20120414 --------- - -Added DCS007. -Handle reopening of the GMSK modem if it disconnects. - -20120419 --------- - -Allow a greater range of GMSK modem addresses. -Replaced NAtools with direct calls to WinUSB :-) -Handle reopening of the DV-RPTR modem if it disconnects, Linux only for now. - -20120420 --------- - -Upgrade the reopening of the DV-RPTR modem on Linux. -Remove the first poll from the repeaters to the gateway. - -20120420a ---------- - -Tweaks to the GMSK modem LibUSB driver to reduce the logging. -Changes to the GMSK modem WinUSB driver. - -20120424 --------- - -Remove the architecture setting in the top level Makefile. -Added DCS004. - -20120428 --------- - -Small revision to the DVAP driver. -Add the -nolog command line option to the Dummy Repeater. - -20120511 --------- - -Fix the -nolog option for the Dummy Repeater. - -20120524 --------- - -Upgraded network error reporting. -Added DCS010. -Removed debug statements from the Slit Repeater. -Add One-Touch Reply to the Dummy Repeater. - -20120526 --------- - -Run the GMSK Modem in a seperate thread. - -20120527 --------- - -Improvements to the changes made in 20120526. - -20120527a ---------- - -Minor tweaks to 20120527 relating to Windows mostly. -Not generally released. - -20120602 --------- - -Remove the LibUsb option for the GMSK modem in Windows. -Infill data gaps with the last transmitted audio or silence. -More GMSK repeater changes. - -20120603 --------- - -Lot of debugging statements in the GMSK drivers. -More GMSK repeater changes. -Removed XRF009, XRF010, XRF011, XRF030, XRF038, and XRF110. -Updated XRF031s and XRF069s IP address. -Added DCS012. - -20120604 --------- - -Remove the GMSK debugging statements. -More GMSK repeater changes. -Fix bug in TX and RX mode in all but the D-Star Repeater. - -20120615 --------- - -Added DCS013. -Roll back GMSK repeater changes regarding driving the modem. -Fixed background data in the GMSK Repeater. - -20120706 --------- - -More GMSK Repeater rollbacks. -Allow cross compilation to ARM processors. -Added DCS011. - -20120802 --------- - -Added DCS014 and DCS015. -Removed XRF100. -Fixed ARM cross compilation location of data files. -Changed silence insertion parameter. -Allow for up to four commands. - -20120826 --------- - -Upgraded to Visual C++ 2010 on Windows. -Platform dependent settings moved to Makefile includes. -Changed the D-Star Repeater to the Sound Card Repeater. -Upgraded to the latest version of the DCS protocol. -Increased the default TX delay in the DV-RPTR Repeater to 150ms from 30ms. - -20120826a ---------- - -Reverted to using Visual C++ 2008 on Windows. - -20120901 --------- - -Allow module E on all of the repeaters. -Re-added Windows LibUSB support to the GMSK Repeater. - -20120920 --------- - -Make LibUsb on both Windows and Linux more agressive at transferring data. -Remove the need for the extra thread in the Linux command line versions. -Initial support for the XDV-RPTR (V2) protocol. -Removed the DCS and DExtra Gateways. -Added SVN revsion version to the log file. -Add AMBE logging to non-GUI versions of the repeaters. -Add -audiodir command line option to change the location of the AMBE data logging. - -20121004 --------- - -Put the DCS and DExtra Gateways back into the package. -Updated DCS protocol. -Added support for the 70cms DVAP. -Use the DVAP to validate its own frequency. -Add a pause in the GMSK Repeater before PTT is set to on. - -20121027 --------- - -Added DCS016, DCS017, and DCS018. -Changed the announcement audio for B and D for UK English. -Removed the beacon in Gateway mode. - -20121112 --------- - -Removed XRF018 and XRF250. -Updated DCS007s IP address. -Added new XDVRPTR data transfer mode for v0.72 firmware or later. - -20121220 --------- - -Added DCS020. -Updated XRF008s IP address. -Add Ethernet support for the DVRPTR-V2 hardware. -Add support for the Raspberry Pi GPIO port. - -20130111 --------- - -Make the frame wait time configurable in the Split Repeater. -Allow for empty beacons in the Analogue Repeater to suppress them. -Convert old DV-RPTR configuration values (Windows only). -Added DCS019. - -20130127 --------- - -Allow for temporary ack text. -Convert the DCS hosts files to use hostnames. -Increase DVAP polling speed to increase reliability on slow machines. -Fixed bug in status reporting. - -20130131 --------- - -Upgrade the DCS Gateway to the latest protocol version. -Revert DVAP polling change. - -20130203 --------- - -Remove DVAP frequency offset setting. - -20130219 --------- - -Updated XRF008s and XRF123s IP addresses. -Reset the DVAP into GMSK mode if FM data is received. -Fix for GMSK modems request for space. -Added DCS021. -Added XRF333. - -20130302 --------- - -Make the DVAP initialisation less aggressive. -Updated to match ircDDB Gateway 20130302. -Added XRF150 - -20130309 --------- - -Allow STN* callsigns as the my callsign. -Allow for switching DTMF blanking off for duplex. - -20130315 --------- - -Added XRF038. -Allow for the new format of Danish callsigns. - -20130323 --------- - -Updated XRF003s IP address. -Added the recording and playback of announcements. - -20130423 --------- - -Change to the wiringPi library rather than bcm2835 library for controlling the RPi. -Added the generic D-Star Repeater and configuration program. -Allow incoming radio audio during the valid_wait period. -Don't reset the announcement timer on transmissions. -Added XRF851. - -20130503 --------- - -Tweaks to the DVAP handling. -Made opening the serial port more aggressive for the DVAP. -Included the D-Star Repeater and Config program in the Windows package for the first time. - -20130508 --------- - -Revert most of the serial port changes. - -20130515 --------- - -Removed aggressive DVAP serial port opening. -Remove support for the DUTCH*Star 0.1.00 firmware. -Add GMSK modem support to the D-Star Repeater. -Added Arduino hardware support. - -20130523 --------- - -Fixed GMSK Modem bug in the D-Star Repeater. -Updated XRF008s IP addresses. -Added XRF727. - -20130615 --------- - -Added XRF250. -Add an optional startup delay for the DV-RPTR V1. -Add a sound card interface to the D-Star Repeater. - -20130622 --------- - -Added XRF310. -Try overlapped serial I/O under Windows again. -Add the serial port PTT control to the D-Star Repeater. -Add /dev/ttyS* devices to the serial control list. -Added a receive level setting to the sound card driver in the D-Star Repeater. -Fixed bugs in the D-Star Repeater regarding end of incoming transmissions. - -20130702 --------- - -Added daemon info to the log. -Changes to the internal buffering. -Remove the Analogue Repeater from the Repeater package. -Remove the DCSGateway, DExtraGateway, and ParrotController from the Repeater package. -Add a DC blocker to the sound card GMSK demodulator. -Implement GMSK PLL locking. - -20130726 --------- - -Restore D-Star Repeater window position on startup. -Allow for the error reply to be disabled. - -20130831 --------- - -Make DV-RPTR V2 handling better. -Catch exceptions and other C++ changes in the D-Star Repeater. -Add TX Delay to the DV-RPTR V2 modem in the D-Star Repeater. - -20130903 --------- - -Removed the DC blocker from the sound card based repeaters. -Remove some of the C++ changes. - -20130904 --------- - -Remove even more of the C++ changes. - -20131231 --------- - -More cleanups. -Removed the white list functionality. -Added explicit support for the DV-RPTR V3 modem. - -20140117 --------- - -Removed the DVAP Node and DV-RPTR Repeater. -Block Australian foundation class and French novice callsigns from using the repeaters. -Removed the optional delay on the DV-RPTR V1 modem. -Added the DVMEGA hardware. - -20140118 --------- - -Fix endian-ness of the DVMEGA frequency command. - -20140128 --------- - -Tweaks to the GMSK Modem controller in the D-Star Repeater. -More work on buffering in the D-Star Repeater for all modem types. - -20140129 --------- - -Move the D-Star Repeater to its own package. -More GMSK Modem changes for better locking. - -20140208 --------- - -GMSK Modem changes. -Improve the DVAP transmit handling. -Added the Split controller. - -20140217 --------- - -Add extra logging to the Split driver. -Remove the FEC restoration in RX only and RX and TX modes. -Added TX Tail to the sound card driver. -Changed the timings in the GMSK driver. -Added GMSK driver modem reopen. -Write the configuration to a file under Windows. - -20140303 --------- - -Experimental DC offset removal in the sound card driver. -Use names in the Split driver to allow for easier matching of addresses and ports. -Increase the number of receivers in the Split driver to five from three. -Added a modem watchdog to the DV-RPTR, DVAP and DVMEGA modem drivers. -Hold off writing the header until the TX is off for the DV-RPTR and DVMEGA modems. - -20140306 --------- - -Removed the modem watchdog. -Fixed the DV-RPTR V2 and V3 modem drivers. -Fixed the DVMEGA driver. -Fix the network name going missing the config program. - -20140410 --------- - -The statistics in the Split driver include all configured receivers. -Put the type of the repeater into the title bar of the GUI. -The first registration packet is after 10 seconds, and 30 seconds thereafter. -Fix many bugs in the Split driver. - -20140415 --------- - -Change the DV-RPTR V2 & V3 handling back to the way it is done in the DV-RPTR Repeater. -Fixed watchdog timer handling in TX Only and TX and RX modes. -Reduce buffering in TX Only and TX and RX modes for local connections. - -20140424 --------- - -Added /dev/ttyACM* for all serial devices under Linux. -Added PTT Inversion control for external hardware controllers. -Hold off the next D-Star transmission until driver is clear of previous data. - -20140520 --------- - -Switch on internal and external speakers for XDVRPTR firmware/hardware. -DVMEGA messages say DVMega now. -Added power setting to the DVMEGA radio. -Add extra TX and RX entries for the Split driver, but not in the GUI. -Add two extra commands. - -20140521 --------- - -Added DVAP error dump. - -20150208 --------- - -Added ALSA for use on Linux, PortAudio is used elsewhere. -Dynamic serial port selection on Linux. -Allow for cross-band operating on the DVMEGA. -GPIO is potentially available on more platforms. -Assert the RTS pin for the DVMEGA at startup. -Getting the version at the startup of a DVMEGA is now more aggressive. -Added a sanity check to the reply from the DVMEGA. -Improved configuration and validation for the DVMEGA. - -20150210 --------- - -Updated the dynamic serial port selection for /dev/ttyACM* -Updated the ALSA handing. - -20150213 --------- - -Updated the ALSA handling once again. -Fixed a compile bug when defining GPIO. - -20150308 --------- - -Added a .mk file for the Pi2. -Increased the network timeout from one second to two seconds. -More changes to the ALSA interface. - -20150404 --------- - -Added method to ALSA interface for TX control in the sound card controller. - -20150615 --------- - -Rearrange the source code slightly. -Allow a DVMEGA to be even slower at responding the first time. - -20150709 --------- - -Allow for a late arriving data sync in the sound card repeater controller. -Add support for D-Star operation with the MMDVM. -Fix bugs in the DV-RPTR V1, V2, V3, and DVMEGA drivers. - -20151001 --------- - -Added the RX and TX levels to the MMDVM driver and configuration. -Change the slow data header replacement in Gateway mode to be more intelligent. -Add sync regeneration to the sound card repeater driver. -Change the slipped sync detection in the sound card repeater driver. -Use a new matched filter in the GMSK modem. -More MMDVM driver changes. - -20151012 --------- - -Change to the Windows serial controller for Windows 10. -Add overflow warnings for the MMDVM. - -2015xxxx --------- - -Fix the TX Delay setting for the MMDVM. - -20180510 --------- - -Port to wxWIdgets-3.0.x - -20180703 --------- - -Add Icom Terminal and Access Point modes. - -20180710 --------- - -Add error recovery to the Icom Terminal and Access Point modes. diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 864d47a..5e6ec45 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -1,29 +1,32 @@ # DStarRepeater Configuration Reference -> **Note:** The config parser treats any line beginning with `#` as a -> comment, and blank lines are skipped. Use `dstarrepeater.example` as -> your starting point for a clean config. +The configuration file uses INI format with `[Section]` headers, `Key=Value` pairs, and `#` comments. The format is compatible with MMDVMHost. -**Config file location:** `/etc/dstarrepeater` -(or `/etc/dstarrepeater_` when using the `-name` command line option) +To get started, copy the example config and edit for your installation: +```bash +sudo cp /etc/dstarrepeater/dstarrepeater.ini.example /etc/dstarrepeater/dstarrepeater.ini +sudo nano /etc/dstarrepeater/dstarrepeater.ini +``` -**Format:** `key=value` — one setting per line, no spaces around the `=`. -Boolean values use `0` for off/false and `1` for on/true. +Then start the daemon: +```bash +dstarrepeaterd /etc/dstarrepeater/dstarrepeater.ini +``` --- -## Callsign Settings +## [General] -| Setting | Description | -|---------|-------------| -| `callsign=GB3IN C` | Repeater callsign, padded to 8 characters. 7-character base callsign + 1-character module suffix (A, B, C, D, or E). | -| `gateway=` | Gateway callsign, padded to 8 characters (e.g. `GB3IN G`). Leave empty if not linked to a gateway. | -| `mode=0` | Operating mode — see [Mode Values](#mode-values) below. | -| `ack=1` | Acknowledgement type — see [Ack Values](#ack-values) below. Forced to `0` in Gateway, TX Only, RX Only, and TX and RX modes. | -| `restriction=0` | `1` = only whitelisted callsigns accepted. | -| `rpt1Validation=1` | `1` = only accept headers addressed to this repeater. Forced to `1` in Gateway mode. | -| `dtmfBlanking=1` | `1` = mute DTMF tones from the audio stream. | -| `errorReply=1` | `1` = reply with an error message for invalid commands. | +| Key | Default | Description | +|-----|---------|-------------| +| `Callsign` | `GB3IN C` | Repeater callsign, padded to 8 characters. 7-character base + 1-character module suffix (A–E). | +| `Gateway` | *(empty)* | Gateway callsign, padded to 8 characters (e.g. `GB3IN G`). Leave empty if not linked. | +| `Mode` | `0` | Operating mode — see [Mode Values](#mode-values) below. | +| `Ack` | `1` | Acknowledgement type — see [Ack Values](#ack-values). Forced to `0` in Gateway, TX Only, RX Only, and TX+RX modes. | +| `Restriction` | `0` | `1` = only whitelisted callsigns accepted. | +| `RPT1Validation` | `1` | `1` = only accept headers addressed to this repeater. | +| `DTMFBlanking` | `1` | `1` = mute DTMF tones from the audio stream. | +| `ErrorReply` | `1` | `1` = reply with error message for invalid commands. | ### Mode Values @@ -32,9 +35,9 @@ Boolean values use `0` for off/false and `1` for on/true. | `0` | Duplex | Full duplex repeater (simultaneous TX and RX) | | `1` | Simplex | Simplex repeater | | `2` | Gateway | Gateway mode (ack forced off, RPT1 validation forced on) | -| `3` | TX Only | Transmit only (ack forced off) | -| `4` | RX Only | Receive only (ack forced off) | -| `5` | TX and RX | Sequential transmit then receive | +| `3` | TX Only | Transmit only | +| `4` | RX Only | Receive only | +| `5` | TX and RX | Sequential transmit then receive (split-site) | ### Ack Values @@ -46,58 +49,82 @@ Boolean values use `0` for off/false and `1` for on/true. --- -## Network Settings +## [Log] -| Setting | Description | -|---------|-------------| -| `gatewayAddress=127.0.0.1` | IP address of the [ircDDBGateway](https://github.com/g4klx/ircDDBGateway) or [DStarGateway](https://github.com/g4klx/DStarGateway). | -| `gatewayPort=20010` | UDP port the gateway is listening on. | -| `localAddress=127.0.0.1` | Local IP address to bind for gateway communication. | -| `localPort=20011` | Local UDP port for gateway communication. | -| `networkName=` | Network name identifier for multi-repeater setups. Leave empty for a single repeater. | +Logging levels match MMDVMHost: `0`=None, `1`=Debug, `2`=Message, `3`=Info, `4`=Warning, `5`=Error, `6`=Fatal. Higher values are more selective (fewer messages). + +| Key | Default | Description | +|-----|---------|-------------| +| `FilePath` | `/var/log` | Directory for daily log files (`dstarrepeaterd-YYYY-MM-DD.log`). | +| `FileLevel` | `2` | Minimum level to write to log file. `0` = no file logging. | +| `DisplayLevel` | `2` | Minimum level to write to stdout. `0` = no stdout output. | +| `MQTTLevel` | `2` | Minimum level to forward to MQTT. `0` = no MQTT logging. | --- -## Modem Type +## [Paths] -| Setting | Description | -|---------|-------------| -| `modemType=DVAP` | The type of modem hardware connected to this repeater. | +| Key | Default | Description | +|-----|---------|-------------| +| `Data` | `/usr/share/dstarrepeater` | Directory containing AMBE voice beacon files (`.ambe` and `.indx`). | +| `Audio` | `/var/log` | Directory for DVTool audio recordings and announcements. | -Must be one of the following strings (case-sensitive): +--- -- `DVAP` -- `DVMEGA` -- `DV-RPTR V1` -- `DV-RPTR V2` -- `DV-RPTR V3` -- `GMSK Modem` -- `MMDVM` -- `Sound Card` -- `Split` -- `Icom Access Point/Terminal Mode` +## [Network] -Only the settings for your selected modem type need to be configured — see [Modem-Specific Settings](#modem-specific-settings). +| Key | Default | Description | +|-----|---------|-------------| +| `GatewayAddress` | `127.0.0.1` | IP address of the gateway (ircDDBGateway or DStarGateway). | +| `GatewayPort` | `20010` | UDP port the gateway is listening on. | +| `LocalAddress` | `127.0.0.1` | Local IP address to bind. | +| `LocalPort` | `20011` | Local UDP port. | +| `Name` | *(empty)* | Network name for multi-repeater setups. | --- -## Timing +## [Modem] + +| Key | Default | Description | +|-----|---------|-------------| +| `Type` | `DVAP` | Hardware modem type (case-sensitive). | + +Valid modem types: -| Setting | Description | -|---------|-------------| -| `timeout=180` | Transmission timeout in seconds. `0` to disable. | -| `ackTime=500` | Acknowledgement delay in milliseconds. | +| Value | Hardware | +|-------|----------| +| `DVAP` | DVAP USB Dongle | +| `DVMEGA` | DV-Mega board | +| `DV-RPTR V1` | DV-RPTR Version 1 | +| `DV-RPTR V2` | DV-RPTR Version 2 | +| `DV-RPTR V3` | DV-RPTR Version 3 | +| `GMSK Modem` | GMSK modem (Dutch*STAR) | +| `MMDVM` | Multi-Mode Digital Voice Modem | +| `Sound Card` | Sound card modem (ALSA/PortAudio) | +| `Split` | Multi-receiver split-site | +| `Icom Access Point/Terminal Mode` | Icom radio in AP/terminal mode | + +Only configure the section matching your modem type. --- -## Beacon +## [Times] + +| Key | Default | Description | +|-----|---------|-------------| +| `Timeout` | `180` | Transmission timeout in seconds. `0` to disable. | +| `AckTime` | `500` | Acknowledgement delay in milliseconds. | + +--- -| Setting | Description | -|---------|-------------| -| `beaconTime=600` | Beacon interval in seconds. `0` to disable. | -| `beaconText=D-Star Repeater` | Text transmitted in the beacon slow data field. | -| `beaconVoice=0` | `1` = transmit a voice beacon in addition to data. | -| `language=0` | Language for voice beacon text-to-speech — see [Language Values](#language-values) below. | +## [Beacon] + +| Key | Default | Description | +|-----|---------|-------------| +| `Time` | `600` | Beacon interval in seconds. `0` to disable. | +| `Text` | `D-Star Repeater` | Text transmitted in the beacon slow data field. | +| `Voice` | `0` | `1` = transmit a voice beacon in addition to data. | +| `Language` | `0` | Language for voice beacon — see [Language Values](#language-values). | ### Language Values @@ -117,233 +144,224 @@ Only the settings for your selected modem type need to be configured — see [Mo --- -## Announcement +## [Announcement] -| Setting | Description | -|---------|-------------| -| `announcementEnabled=0` | `1` = enable the announcement/recording feature. | -| `announcementTime=500` | Announcement playback interval in milliseconds. | -| `announcementRecordRPT1=` | RPT1 callsign that triggers recording. | -| `announcementRecordRPT2=` | RPT2 callsign that triggers recording. | -| `announcementDeleteRPT1=` | RPT1 callsign that triggers deletion. | -| `announcementDeleteRPT2=` | RPT2 callsign that triggers deletion. | +| Key | Default | Description | +|-----|---------|-------------| +| `Enabled` | `0` | `1` = enable announcement recording and playback. | +| `Time` | `500` | Announcement playback interval in seconds. | +| `RecordRPT1` | *(empty)* | RPT1 callsign that triggers recording. | +| `RecordRPT2` | *(empty)* | RPT2 callsign that triggers recording. | +| `DeleteRPT1` | *(empty)* | RPT1 callsign that triggers deletion. | +| `DeleteRPT2` | *(empty)* | RPT2 callsign that triggers deletion. | --- -## DTMF Control - -| Setting | Description | -|---------|-------------| -| `controlEnabled=0` | `1` = enable DTMF remote control. | -| `controlRPT1=` | RPT1 callsign required for control commands. | -| `controlRPT2=` | RPT2 callsign required for control commands. | -| `controlShutdown=` | DTMF sequence to remotely shut down the repeater. | -| `controlStartup=` | DTMF sequence to remotely start up the repeater. | -| `controlStatus1=` | DTMF sequence to trigger status message 1. | -| `controlStatus2=` | DTMF sequence to trigger status message 2. | -| `controlStatus3=` | DTMF sequence to trigger status message 3. | -| `controlStatus4=` | DTMF sequence to trigger status message 4. | -| `controlStatus5=` | DTMF sequence to trigger status message 5. | -| `controlCommand1=` | DTMF sequence for custom command 1. | -| `controlCommand1Line=` | Shell command executed when command 1 is received. | -| `controlCommand2=` | DTMF sequence for custom command 2. | -| `controlCommand2Line=` | Shell command executed when command 2 is received. | -| `controlCommand3=` | DTMF sequence for custom command 3. | -| `controlCommand3Line=` | Shell command executed when command 3 is received. | -| `controlCommand4=` | DTMF sequence for custom command 4. | -| `controlCommand4Line=` | Shell command executed when command 4 is received. | -| `controlCommand5=` | DTMF sequence for custom command 5. | -| `controlCommand5Line=` | Shell command executed when command 5 is received. | -| `controlCommand6=` | DTMF sequence for custom command 6. | -| `controlCommand6Line=` | Shell command executed when command 6 is received. | -| `controlOutput1=` | DTMF sequence to toggle output relay 1. | -| `controlOutput2=` | DTMF sequence to toggle output relay 2. | -| `controlOutput3=` | DTMF sequence to toggle output relay 3. | -| `controlOutput4=` | DTMF sequence to toggle output relay 4. | +## [Control] + +DTMF remote control via D-Star headers. When enabled, specific YOURCALL values trigger actions. + +| Key | Default | Description | +|-----|---------|-------------| +| `Enabled` | `0` | `1` = enable DTMF remote control. | +| `RPT1` | *(empty)* | RPT1 callsign required for control commands. | +| `RPT2` | *(empty)* | RPT2 callsign required for control commands. | +| `Shutdown` | *(empty)* | YOURCALL value to shut down the repeater. | +| `Startup` | *(empty)* | YOURCALL value to start up the repeater. | +| `Status1`–`Status5` | *(empty)* | YOURCALL values to trigger status messages 1–5. | +| `Command1`–`Command6` | *(empty)* | YOURCALL values for custom commands 1–6. | +| `Command1Line`–`Command6Line` | *(empty)* | Shell command executed when the corresponding command is triggered. | +| `Output1`–`Output4` | *(empty)* | YOURCALL values to toggle output relays 1–4. | --- -## External Controller +## [Controller] + +External hardware controller for PTT, LEDs, and relay outputs. -| Setting | Description | -|---------|-------------| -| `controllerType=` | Hardware controller type for PTT, LEDs, and relays — see [Controller Types](#controller-types) below. Leave empty for none. | -| `serialConfig=1` | Hardware config selection (1–5). Selects pin/output mappings on the controller. | -| `pttInvert=0` | `1` = invert PTT output (active low). | -| `activeHangTime=0` | Seconds to hold active state after last transmission ends. `0` to disable. | +| Key | Default | Description | +|-----|---------|-------------| +| `Type` | *(empty)* | Controller type — see [Controller Types](#controller-types). | +| `SerialConfig` | `1` | Pin/output configuration preset (1–5). | +| `PTTInvert` | `0` | `1` = invert PTT output (active low). | +| `ActiveHangTime` | `0` | Seconds to hold active state after last transmission. `0` to disable. | ### Controller Types | Value | Description | |-------|-------------| -| *(empty)* | No controller | +| *(empty)* | No controller (dummy) | | `GPIO` | Raspberry Pi GPIO pins | | `UDRC` | UDRC board (Raspberry Pi) | -| `Velleman K8055 - 0` | Velleman K8055 USB board at address 0–3 | -| `URI USB - 1` | URI USB relay card at address 1–6 | +| `Velleman K8055 - 0` | Velleman K8055 USB board (address 0–3) | +| `URI USB - 1` | URI USB relay card (address 1–6) | | `Serial - /dev/ttyUSB0` | Serial port controller | | `Arduino - /dev/ttyUSB0` | Arduino controller via serial | --- -## Output Relay Defaults +## [Outputs] -| Setting | Description | -|---------|-------------| -| `output1=0` | Default state of output relay 1 at startup. `1` = on. | -| `output2=0` | Default state of output relay 2 at startup. `1` = on. | -| `output3=0` | Default state of output relay 3 at startup. `1` = on. | -| `output4=0` | Default state of output relay 4 at startup. `1` = on. | +Default state of output relays at startup. + +| Key | Default | Description | +|-----|---------|-------------| +| `Output1` | `0` | Output relay 1. `1` = on at startup. | +| `Output2` | `0` | Output relay 2. | +| `Output3` | `0` | Output relay 3. | +| `Output4` | `0` | Output relay 4. | --- -## Logging +## [Frame Logging] -| Setting | Description | -|---------|-------------| -| `logging=0` | `1` = enable logging to file. | +| Key | Default | Description | +|-----|---------|-------------| +| `Enabled` | `0` | `1` = record all D-Star frames to `.dvtool` files in the audio directory. | --- -## Window Position (GUI only) +## [Whitelist] / [Blacklist] / [Greylist] + +Callsign access control lists. Each takes a single `File` key pointing to a text file with one callsign per line. + +| Key | Default | Description | +|-----|---------|-------------| +| `File` | *(not set)* | Full path to the list file. Leave commented out to disable. | -| Setting | Description | -|---------|-------------| -| `windowX=-1` | Window X position. `-1` for system default. | -| `windowY=-1` | Window Y position. `-1` for system default. | +Example: +```ini +[Whitelist] +File=/etc/dstarrepeater/rptr_whitelist.dat +``` --- ## Modem-Specific Settings -Only configure the section that matches your `modemType`. - -### DVAP - -| Setting | Description | -|---------|-------------| -| `dvapPort=` | Serial port (e.g. `/dev/ttyUSB0`). | -| `dvapFrequency=145500000` | Operating frequency in Hz (e.g. `438500000` = 438.500 MHz). | -| `dvapPower=10` | Transmit power in dBm. | -| `dvapSquelch=-100` | Squelch level in dBm. Signals below this are ignored. | - -### GMSK Modem - -| Setting | Description | -|---------|-------------| -| `gmskAddress=768` | USB address of the GMSK modem (decimal). Default 768 = 0x0300 hex. | - -### DV-RPTR V1 - -| Setting | Description | -|---------|-------------| -| `dvrptr1Port=` | Serial port (e.g. `/dev/ttyUSB0`). | -| `dvrptr1RXInvert=0` | `1` = invert received signal polarity. | -| `dvrptr1TXInvert=0` | `1` = invert transmitted signal polarity. | -| `dvrptr1Channel=0` | `0` = Channel A, `1` = Channel B. | -| `dvrptr1ModLevel=20` | Modulation level (0–100%). | -| `dvrptr1TXDelay=150` | Transmit delay in milliseconds. | - -### DV-RPTR V2 - -| Setting | Description | -|---------|-------------| -| `dvrptr2Connection=0` | `0` = USB, `1` = Network. | -| `dvrptr2USBPort=` | USB serial port (when connection = `0`). | -| `dvrptr2Address=127.0.0.1` | Network address (when connection = `1`). | -| `dvrptr2Port=0` | Network port (when connection = `1`). | -| `dvrptr2TXInvert=0` | `1` = invert transmitted signal polarity. | -| `dvrptr2ModLevel=20` | Modulation level (0–100%). | -| `dvrptr2TXDelay=150` | Transmit delay in milliseconds. | - -### DV-RPTR V3 - -| Setting | Description | -|---------|-------------| -| `dvrptr3Connection=0` | `0` = USB, `1` = Network. | -| `dvrptr3USBPort=` | USB serial port (when connection = `0`). | -| `dvrptr3Address=127.0.0.1` | Network address (when connection = `1`). | -| `dvrptr3Port=0` | Network port (when connection = `1`). | -| `dvrptr3TXInvert=0` | `1` = invert transmitted signal polarity. | -| `dvrptr3ModLevel=20` | Modulation level (0–100%). | -| `dvrptr3TXDelay=150` | Transmit delay in milliseconds. | - -### DVMEGA - -| Setting | Description | -|---------|-------------| -| `dvmegaPort=` | Serial port (e.g. `/dev/ttyAMA0`). | -| `dvmegaVariant=0` | `0` = Modem, `1` = Radio 2m, `2` = Radio 70cm, `3` = Radio 2m/70cm. | -| `dvmegaRXInvert=0` | `1` = invert received signal polarity. | -| `dvmegaTXInvert=0` | `1` = invert transmitted signal polarity. | -| `dvmegaTXDelay=150` | Transmit delay in milliseconds. | -| `dvmegaRXFrequency=145500000` | Receive frequency in Hz (radio variants only). | -| `dvmegaTXFrequency=145500000` | Transmit frequency in Hz (radio variants only). | -| `dvmegaPower=100` | Transmit power as a percentage (0–100%, radio variants only). | - -### MMDVM - -| Setting | Description | -|---------|-------------| -| `mmdvmPort=` | Serial port (e.g. `/dev/ttyACM0`). | -| `mmdvmRXInvert=0` | `1` = invert received signal polarity. | -| `mmdvmTXInvert=0` | `1` = invert transmitted signal polarity. | -| `mmdvmPTTInvert=0` | `1` = invert PTT signal. | -| `mmdvmTXDelay=50` | Transmit delay in milliseconds. | -| `mmdvmRXLevel=100` | Receive audio level (0–100%). | -| `mmdvmTXLevel=100` | Transmit audio level (0–100%). | - -### Sound Card - -| Setting | Description | -|---------|-------------| -| `soundCardRXDevice=` | Sound card device name for receiving audio. | -| `soundCardTXDevice=` | Sound card device name for transmitting audio. | -| `soundCardRXInvert=0` | `1` = invert received signal polarity. | -| `soundCardTXInvert=0` | `1` = invert transmitted signal polarity. | -| `soundCardRXLevel=1.0000` | Receive level multiplier (1.0 = unity gain). | -| `soundCardTXLevel=1.0000` | Transmit level multiplier (1.0 = unity gain). | -| `soundCardTXDelay=150` | Transmit delay in milliseconds. | -| `soundCardTXTail=50` | Transmit tail in milliseconds (extra carrier after last frame). | - -### Icom Access Point/Terminal Mode - -| Setting | Description | -|---------|-------------| -| `icomPort=` | Serial port (e.g. `/dev/ttyUSB0`). | - -### Split - -| Setting | Description | -|---------|-------------| -| `splitLocalAddress=` | Local IP address for split RX/TX communication. | -| `splitLocalPort=0` | Local UDP port for split communication. | -| `splitTimeout=0` | Split timeout in seconds. `0` to disable. | - -Split TX/RX names are stored as indexed entries (`splitTXName0`, `splitRXName0`, etc.) — up to 5 transmitters and 25 receivers. +Only configure the section matching your `[Modem] Type`. + +### [DVAP] + +| Key | Default | Description | +|-----|---------|-------------| +| `Port` | *(empty)* | Serial port (e.g. `/dev/ttyUSB0`). | +| `Frequency` | `145500000` | Operating frequency in Hz. | +| `Power` | `10` | Transmit power in dBm. | +| `Squelch` | `-100` | Squelch threshold in dBm. | + +### [GMSK] + +| Key | Default | Description | +|-----|---------|-------------| +| `InterfaceType` | `0` | `0` = libUSB, `1` = direct/serial. | +| `Address` | `768` | USB address in decimal (768 = 0x0300). | + +### [DV-RPTR V1] + +| Key | Default | Description | +|-----|---------|-------------| +| `Port` | *(empty)* | Serial port (e.g. `/dev/ttyUSB0`). | +| `RXInvert` | `0` | `1` = invert received signal polarity. | +| `TXInvert` | `0` | `1` = invert transmitted signal polarity. | +| `Channel` | `0` | `0` = Channel A, `1` = Channel B. | +| `ModLevel` | `20` | Modulation level (0–100%). | +| `TXDelay` | `150` | Transmit delay in milliseconds. | + +### [DV-RPTR V2] + +| Key | Default | Description | +|-----|---------|-------------| +| `Connection` | `0` | `0` = USB, `1` = Network. | +| `USBPort` | *(empty)* | USB serial port. | +| `Address` | `127.0.0.1` | Network address (when Connection=1). | +| `Port` | `0` | Network port (when Connection=1). | +| `TXInvert` | `0` | `1` = invert transmitted signal polarity. | +| `ModLevel` | `20` | Modulation level (0–100%). | +| `TXDelay` | `150` | Transmit delay in milliseconds. | + +### [DV-RPTR V3] + +Same keys as [DV-RPTR V2]. + +### [DVMEGA] + +| Key | Default | Description | +|-----|---------|-------------| +| `Port` | *(empty)* | Serial port (e.g. `/dev/ttyAMA0`). | +| `Variant` | `0` | `0` = Modem, `1` = Radio 2m, `2` = Radio 70cm, `3` = Radio 2m/70cm. | +| `RXInvert` | `0` | `1` = invert received signal. | +| `TXInvert` | `0` | `1` = invert transmitted signal. | +| `TXDelay` | `150` | Transmit delay in milliseconds. | +| `RXFrequency` | `145500000` | Receive frequency in Hz (radio variants only). | +| `TXFrequency` | `145500000` | Transmit frequency in Hz (radio variants only). | +| `Power` | `100` | Transmit power 0–100% (radio variants only). | + +### [MMDVM] + +| Key | Default | Description | +|-----|---------|-------------| +| `Port` | *(empty)* | Serial port (e.g. `/dev/ttyACM0`). | +| `RXInvert` | `0` | `1` = invert received signal. | +| `TXInvert` | `0` | `1` = invert transmitted signal. | +| `PTTInvert` | `0` | `1` = invert PTT signal. | +| `TXDelay` | `50` | Transmit delay in milliseconds. | +| `RXLevel` | `100` | Receive level (0–100%). | +| `TXLevel` | `100` | Transmit level (0–100%). | + +### [Sound Card] + +| Key | Default | Description | +|-----|---------|-------------| +| `RXDevice` | *(empty)* | Sound card device name for receiving. | +| `TXDevice` | *(empty)* | Sound card device name for transmitting. | +| `RXInvert` | `0` | `1` = invert received signal. | +| `TXInvert` | `0` | `1` = invert transmitted signal. | +| `RXLevel` | `1.0` | Receive level multiplier (1.0 = unity gain). | +| `TXLevel` | `1.0` | Transmit level multiplier (1.0 = unity gain). | +| `TXDelay` | `150` | Transmit delay in milliseconds. | +| `TXTail` | `50` | Transmit tail in milliseconds (extra carrier after last frame). | + +### [Split] + +Multi-receiver split-site configuration using UDP. + +| Key | Default | Description | +|-----|---------|-------------| +| `LocalAddress` | *(empty)* | Local IP address for split communication. | +| `LocalPort` | `0` | Local UDP port. | +| `Timeout` | `0` | Timeout in milliseconds. `0` to disable. | + +TX/RX names are configured as indexed keys: `TXName0`–`TXName4` and `RXName0`–`RXName24`. + +### [Icom] + +| Key | Default | Description | +|-----|---------|-------------| +| `Port` | *(empty)* | Serial port (e.g. `/dev/ttyUSB0`). | --- -## MQTT Settings +## [MQTT] -*Only used when built with `make MQTT=1`. See [MQTT.md](MQTT.md) for full details on topics, JSON format, and Display-Driver compatibility.* +*Only used when built with `make MQTT=1`. See [MQTT.md](MQTT.md) for full details.* -| Setting | Description | -|---------|-------------| -| `mqttHost=127.0.0.1` | MQTT broker hostname or IP address. Set to empty to disable MQTT at runtime even when compiled in. | -| `mqttPort=1883` | MQTT broker port. | -| `mqttAuth=0` | `1` = authenticate with the broker using `mqttUsername` and `mqttPassword`. | -| `mqttUsername=` | Broker username (when `mqttAuth=1`). | -| `mqttPassword=` | Broker password (when `mqttAuth=1`). | -| `mqttKeepalive=60` | Keepalive interval in seconds. Minimum 5. | -| `mqttName=dstar-repeater` | Client name and topic prefix for all published messages. | +| Key | Default | Description | +|-----|---------|-------------| +| `Host` | `127.0.0.1` | MQTT broker hostname or IP. Empty to disable MQTT at runtime. | +| `Port` | `1883` | Broker port. | +| `Auth` | `0` | `1` = authenticate with username/password. | +| `Username` | *(empty)* | Broker username (when Auth=1). | +| `Password` | *(empty)* | Broker password (when Auth=1). | +| `Keepalive` | `60` | Keepalive interval in seconds. | +| `Name` | `dstar-repeater` | Client name and topic prefix. | ### MQTT Topics -With the default `mqttName` of `dstar-repeater`, the following topics are published: +With `Name=dstar-repeater`: | Topic | Content | |-------|---------| -| `dstar-repeater/log` | Timestamped log messages, filtered by severity. | -| `dstar-repeater/status` | JSON repeater status, published once per second. | -| `dstar-repeater/json` | Display-Driver-compatible events at state transitions. | +| `dstar-repeater/log` | Timestamped log messages (filtered by MQTTLevel). | +| `dstar-repeater/status` | JSON repeater status (1/sec). | +| `dstar-repeater/json` | Display-Driver-compatible D-Star events. | diff --git a/Common/AMBEFEC.cpp b/Common/AMBEFEC.cpp index a672b51..a010833 100644 --- a/Common/AMBEFEC.cpp +++ b/Common/AMBEFEC.cpp @@ -18,7 +18,8 @@ #include "AMBEFEC.h" -#include +#include +#include static const unsigned int PRNG_TABLE[] = { 0x42CC47U, 0x19D6FEU, 0x304729U, 0x6B2CD0U, 0x60BF47U, 0x39650EU, 0x7354F1U, 0xEACF60U, 0x819C9FU, 0xDE25CEU, @@ -442,7 +443,7 @@ CAMBEFEC::~CAMBEFEC() unsigned int CAMBEFEC::regenerate(unsigned char* bytes) const { - wxASSERT(bytes != NULL); + assert(bytes != nullptr); unsigned int a = ((bytes[0] & 0x80) ? 0x800000 : 0x000000) | ((bytes[0] & 0x02) ? 0x400000 : 0x000000) | ((bytes[1] & 0x08) ? 0x200000 : 0x000000) | ((bytes[2] & 0x20) ? 0x100000 : 0x000000) | @@ -526,7 +527,7 @@ unsigned int CAMBEFEC::regenerate(unsigned char* bytes) const unsigned int CAMBEFEC::count(const unsigned char* bytes) const { - wxASSERT(bytes != NULL); + assert(bytes != nullptr); unsigned int a = ((bytes[0] & 0x80) ? 0x800000 : 0x000000) | ((bytes[0] & 0x02) ? 0x400000 : 0x000000) | ((bytes[1] & 0x08) ? 0x200000 : 0x000000) | ((bytes[2] & 0x20) ? 0x100000 : 0x000000) | diff --git a/Common/AMBEFEC.h b/Common/AMBEFEC.h index 8f9cf8c..9888cc2 100644 --- a/Common/AMBEFEC.h +++ b/Common/AMBEFEC.h @@ -21,12 +21,23 @@ #include "Golay.h" +/* + * Forward error correction for AMBE codec frames. + * + * D-Star uses AMBE+2 (72-bit frames) with a Golay(23,12) FEC scheme applied + * to the most significant bits of each frame. regenerate() attempts to repair + * bit errors in the three Golay codewords that make up one AMBE frame, and + * returns the number of errors that were corrected. count() returns the number + * of uncorrectable errors without modifying the data. + */ class CAMBEFEC { public: CAMBEFEC(); ~CAMBEFEC(); + // Attempts in-place error correction; returns number of errors corrected. unsigned int regenerate(unsigned char* bytes) const; + // Counts bit errors without modifying the data. unsigned int count(const unsigned char* bytes) const; private: diff --git a/Common/AnnouncementCallback.h b/Common/AnnouncementCallback.h index eef53f2..a6bc3d6 100644 --- a/Common/AnnouncementCallback.h +++ b/Common/AnnouncementCallback.h @@ -16,6 +16,8 @@ #include "HeaderData.h" +// Callback interface implemented by the repeater thread. +// CAnnouncementUnit calls these to key up the transmitter and push announcement DV frames. class IAnnouncementCallback { public: virtual void transmitAnnouncementHeader(CHeaderData* header) = 0; diff --git a/Common/AnnouncementUnit.cpp b/Common/AnnouncementUnit.cpp index 89430cf..e1340c6 100644 --- a/Common/AnnouncementUnit.cpp +++ b/Common/AnnouncementUnit.cpp @@ -18,12 +18,20 @@ #include "AnnouncementUnit.h" -#include -#include +#include +#include +#include +#if defined(_WIN32) +#include +#define access _access +#define F_OK 0 +#else +#include +#endif -const wxString GLOBAL_FILE_NAME = wxT("Announce"); +static const std::string GLOBAL_FILE_NAME = "Announce"; -CAnnouncementUnit::CAnnouncementUnit(IAnnouncementCallback* handler, const wxString& callsign) : +CAnnouncementUnit::CAnnouncementUnit(IAnnouncementCallback* handler, const std::string& callsign) : m_handler(handler), m_localFileName(), m_reader(), @@ -32,14 +40,23 @@ m_time(), m_out(0U), m_sending(false) { - wxASSERT(handler != NULL); + assert(handler != nullptr); - m_localFileName.Printf(wxT("Announce %s"), callsign.c_str()); -#if !defined(__WINDOWS__) - m_localFileName.Replace(wxT(" "), wxT("_")); -#endif + m_localFileName = "Announce " + callsign; + + // Replace spaces with underscores in the filename + size_t pos = 0; + while ((pos = m_localFileName.find(' ', pos)) != std::string::npos) { + m_localFileName.replace(pos, 1, "_"); + pos += 1; + } - m_writer.setDirectory(wxFileName::GetHomeDir()); +#if defined(_WIN32) + const char* home = getenv("USERPROFILE"); +#else + const char* home = getenv("HOME"); +#endif + m_writer.setDirectory(std::string(home != nullptr ? home : "")); } CAnnouncementUnit::~CAnnouncementUnit() @@ -65,23 +82,33 @@ bool CAnnouncementUnit::writeData(const unsigned char* data, unsigned int length void CAnnouncementUnit::deleteAnnouncement() { - wxFileName fileName(wxFileName::GetHomeDir(), m_localFileName, wxT("dvtool")); +#if defined(_WIN32) + const char* home = getenv("USERPROFILE"); +#else + const char* home = getenv("HOME"); +#endif + std::string filePath = std::string(home != nullptr ? home : "") + "/" + m_localFileName + ".dvtool"; - if (wxFile::Exists(fileName.GetFullPath())) - ::wxRemoveFile(fileName.GetFullPath()); + if (access(filePath.c_str(), F_OK) == 0) + ::remove(filePath.c_str()); } void CAnnouncementUnit::startAnnouncement() { - wxFileName fileName1(wxFileName::GetHomeDir(), m_localFileName, wxT("dvtool")); - wxFileName fileName2(wxFileName::GetHomeDir(), GLOBAL_FILE_NAME, wxT("dvtool")); +#if defined(_WIN32) + const char* home = getenv("USERPROFILE"); +#else + const char* home = getenv("HOME"); +#endif + std::string filePath1 = std::string(home != nullptr ? home : "") + "/" + m_localFileName + ".dvtool"; + std::string filePath2 = std::string(home != nullptr ? home : "") + "/" + GLOBAL_FILE_NAME + ".dvtool"; - if (wxFile::Exists(fileName1.GetFullPath())) { - bool ret = m_reader.open(fileName1.GetFullPath()); + if (access(filePath1.c_str(), F_OK) == 0) { + bool ret = m_reader.open(filePath1); if (!ret) return; - } else if (wxFile::Exists(fileName2.GetFullPath())) { - bool ret = m_reader.open(fileName2.GetFullPath()); + } else if (access(filePath2.c_str(), F_OK) == 0) { + bool ret = m_reader.open(filePath2); if (!ret) return; } else { @@ -90,14 +117,12 @@ void CAnnouncementUnit::startAnnouncement() DVTFR_TYPE type = m_reader.read(); if (type != DVTFR_HEADER) { - wxLogError(wxT("Invalid header element in the file - %d"), int(type)); m_reader.close(); return; } CHeaderData* header = m_reader.readHeader(); - if (header == NULL) { - wxLogError(wxT("NULL header element in the file")); + if (header == nullptr) { m_reader.close(); return; } @@ -107,7 +132,7 @@ void CAnnouncementUnit::startAnnouncement() m_handler->transmitAnnouncementHeader(header); - m_time.Start(); + m_time = std::chrono::steady_clock::now(); m_out = 0U; m_sending = true; @@ -118,12 +143,11 @@ void CAnnouncementUnit::clock() if (!m_sending) return; - unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS; + unsigned int needed = std::chrono::duration_cast(std::chrono::steady_clock::now() - m_time).count() / DSTAR_FRAME_TIME_MS; while (m_out < needed) { DVTFR_TYPE type = m_reader.read(); if (type != DVTFR_DATA) { - wxLogError(wxT("Invalid data element in the file - %d"), int(type)); m_handler->transmitAnnouncementData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); m_reader.close(); m_sending = false; diff --git a/Common/AnnouncementUnit.h b/Common/AnnouncementUnit.h index 0823214..19db94d 100644 --- a/Common/AnnouncementUnit.h +++ b/Common/AnnouncementUnit.h @@ -25,11 +25,29 @@ #include "DStarDefines.h" #include "HeaderData.h" -#include +#include "StdCompat.h" +#include +/* + * Records and plays back user announcements stored as .dvtool files. + * + * Lifecycle: + * Record — The repeater thread calls writeHeader() then writeData() as + * audio arrives over RF. The stream is saved to a per-callsign + * file ("Announce_.dvtool") in the user's home directory. + * + * Playback — startAnnouncement() opens the callsign-specific file if it + * exists, falling back to the global "Announce.dvtool". It reads + * the header and signals the repeater thread to key up. clock() + * then releases DV frames at air rate (same wall-clock pacing as + * CBeaconUnit) until the end-of-transmission marker is reached. + * + * Delete — deleteAnnouncement() removes the callsign-specific file so the + * global announcement (or silence) takes effect on the next play. + */ class CAnnouncementUnit { public: - CAnnouncementUnit(IAnnouncementCallback* handler, const wxString& callsign); + CAnnouncementUnit(IAnnouncementCallback* handler, const std::string& callsign); ~CAnnouncementUnit(); bool writeHeader(const CHeaderData& header); @@ -39,15 +57,16 @@ public: void startAnnouncement(); + // Called every repeater tick; releases frames to the transmitter at air rate. void clock(); private: IAnnouncementCallback* m_handler; - wxString m_localFileName; + std::string m_localFileName; // Callsign-specific filename (no extension). CDVTOOLFileReader m_reader; CDVTOOLFileWriter m_writer; - wxStopWatch m_time; - unsigned int m_out; + std::chrono::steady_clock::time_point m_time; + unsigned int m_out; // Frames dispatched so far during playback. bool m_sending; }; diff --git a/Common/ArduinoController.cpp b/Common/ArduinoController.cpp index 8b4ef04..94c1493 100644 --- a/Common/ArduinoController.cpp +++ b/Common/ArduinoController.cpp @@ -30,7 +30,7 @@ const char OUT_PORT7 = 0x40U; const char OUT_PORT8 = 0x80U; -CArduinoController::CArduinoController(const wxString& port) : +CArduinoController::CArduinoController(const std::string& port) : m_serial(port, SERIAL_19200), m_out(0x00U), m_in(0x00U) diff --git a/Common/ArduinoController.h b/Common/ArduinoController.h index bbf0889..076511b 100644 --- a/Common/ArduinoController.h +++ b/Common/ArduinoController.h @@ -16,13 +16,12 @@ #include "SerialDataController.h" #include "HardwareController.h" - -#include +#include "StdCompat.h" class CArduinoController : public IHardwareController { public: - CArduinoController(const wxString& port); + CArduinoController(const std::string& port); virtual ~CArduinoController(); virtual bool open(); diff --git a/Common/AudioCallback.h b/Common/AudioCallback.h index 3259369..de6fcd3 100644 --- a/Common/AudioCallback.h +++ b/Common/AudioCallback.h @@ -14,12 +14,13 @@ #ifndef AudioCallback_H #define AudioCallback_H -#include - +// Callback interface implemented by the sound card modem. +// readCallback() delivers captured RX audio samples; writeCallback() requests +// TX audio samples to be filled into the output buffer. class IAudioCallback { public: - virtual void readCallback(const wxFloat32* input, unsigned int nSamples, int id) = 0; - virtual void writeCallback(wxFloat32* output, int& nSamples, int id) = 0; + virtual void readCallback(const float* input, unsigned int nSamples, int id) = 0; + virtual void writeCallback(float* output, int& nSamples, int id) = 0; private: }; diff --git a/Common/BeaconCallback.h b/Common/BeaconCallback.h index 2ba6aa8..20c3f52 100644 --- a/Common/BeaconCallback.h +++ b/Common/BeaconCallback.h @@ -14,6 +14,8 @@ #ifndef BeaconCallback_H #define BeaconCallback_H +// Callback interface implemented by the repeater thread. +// CBeaconUnit calls these to key up the transmitter and push beacon DV frames. class IBeaconCallback { public: virtual void transmitBeaconHeader() = 0; diff --git a/Common/BeaconUnit.cpp b/Common/BeaconUnit.cpp index 3b591ca..964addf 100644 --- a/Common/BeaconUnit.cpp +++ b/Common/BeaconUnit.cpp @@ -18,19 +18,31 @@ #include "BeaconUnit.h" -#include -#include -#include -#include +#include +#include +#include +#include +#include +#if defined(_WIN32) +#include +#define access _access +#define F_OK 0 +#define R_OK 4 +#else +#include +#endif +// Maximum beacon duration: 60 seconds at the D-Star frame rate. const unsigned int MAX_FRAMES = 60U * DSTAR_FRAMES_PER_SEC; +// Number of silent AMBE frames prepended to the AMBE buffer and mapped to " ". +// These create a short pause before and after each word/character. const unsigned int SILENCE_LENGTH = 10U; -CBeaconUnit::CBeaconUnit(IBeaconCallback* handler, const wxString& callsign, const wxString& text, bool voice, TEXT_LANG language) : -m_ambe(NULL), +CBeaconUnit::CBeaconUnit(IBeaconCallback* handler, const std::string& callsign, const std::string& text, bool voice, TEXT_LANG language) : +m_ambe(nullptr), m_ambeLength(0U), -m_data(NULL), +m_data(nullptr), m_dataLength(0U), m_index(), m_language(language), @@ -43,69 +55,69 @@ m_seqNo(0U), m_time(), m_sending(false) { - wxASSERT(handler != NULL); + assert(handler != nullptr); - wxString slowData = text; - slowData.resize(20U, wxT(' ')); + std::string slowData = text; + slowData.resize(20U, ' '); m_encoder.setTextData(slowData); - m_data = new unsigned char[MAX_FRAMES * VOICE_FRAME_LENGTH_BYTES]; - ::memset(m_data, 0x00U, MAX_FRAMES * VOICE_FRAME_LENGTH_BYTES); + m_data = new unsigned char[MAX_FRAMES * DV_FRAME_LENGTH_BYTES]; + ::memset(m_data, 0x00U, MAX_FRAMES * DV_FRAME_LENGTH_BYTES); if (!voice) return; - wxString ambeFileName; - wxString indxFileName; + std::string ambeFileName; + std::string indxFileName; switch (m_language) { case TL_DEUTSCH: - ambeFileName = wxT("de_DE.ambe"); - indxFileName = wxT("de_DE.indx"); + ambeFileName = "de_DE.ambe"; + indxFileName = "de_DE.indx"; break; case TL_DANSK: - ambeFileName = wxT("dk_DK.ambe"); - indxFileName = wxT("dk_DK.indx"); + ambeFileName = "dk_DK.ambe"; + indxFileName = "dk_DK.indx"; break; case TL_ITALIANO: - ambeFileName = wxT("it_IT.ambe"); - indxFileName = wxT("it_IT.indx"); + ambeFileName = "it_IT.ambe"; + indxFileName = "it_IT.indx"; break; case TL_FRANCAIS: - ambeFileName = wxT("fr_FR.ambe"); - indxFileName = wxT("fr_FR.indx"); + ambeFileName = "fr_FR.ambe"; + indxFileName = "fr_FR.indx"; break; case TL_ESPANOL: - ambeFileName = wxT("es_ES.ambe"); - indxFileName = wxT("es_ES.indx"); + ambeFileName = "es_ES.ambe"; + indxFileName = "es_ES.indx"; break; case TL_SVENSKA: - ambeFileName = wxT("se_SE.ambe"); - indxFileName = wxT("se_SE.indx"); + ambeFileName = "se_SE.ambe"; + indxFileName = "se_SE.indx"; break; case TL_POLSKI: - ambeFileName = wxT("pl_PL.ambe"); - indxFileName = wxT("pl_PL.indx"); + ambeFileName = "pl_PL.ambe"; + indxFileName = "pl_PL.indx"; break; case TL_ENGLISH_US: - ambeFileName = wxT("en_US.ambe"); - indxFileName = wxT("en_US.indx"); + ambeFileName = "en_US.ambe"; + indxFileName = "en_US.indx"; break; case TL_NORSK: - ambeFileName = wxT("no_NO.ambe"); - indxFileName = wxT("no_NO.indx"); + ambeFileName = "no_NO.ambe"; + indxFileName = "no_NO.indx"; break; // case TL_NEDERLANDS_NL: -// ambeFileName = wxT("nl_NL.ambe"); -// indxFileName = wxT("nl_NL.indx"); +// ambeFileName = "nl_NL.ambe"; +// indxFileName = "nl_NL.indx"; // break; // case TL_NEDERLANDS_BE: -// ambeFileName = wxT("nl_BE.ambe"); -// indxFileName = wxT("nl_BE.indx"); +// ambeFileName = "nl_BE.ambe"; +// indxFileName = "nl_BE.indx"; // break; default: - ambeFileName = wxT("en_GB.ambe"); - indxFileName = wxT("en_GB.indx"); + ambeFileName = "en_GB.ambe"; + indxFileName = "en_GB.indx"; break; } @@ -131,14 +143,14 @@ void CBeaconUnit::sendBeacon() m_sending = true; - m_time.Start(); + m_time = std::chrono::steady_clock::now(); m_in = 0U; m_out = 0U; m_seqNo = 0U; m_dataLength = 0U; - if (m_ambe == NULL) { + if (m_ambe == nullptr) { for (unsigned int i = 0U; i < 21U; i++) { unsigned char buffer[DV_FRAME_LENGTH_BYTES]; @@ -160,17 +172,17 @@ void CBeaconUnit::sendBeacon() m_in++; } } else { - lookup(wxT(" ")); - lookup(wxT(" ")); - lookup(wxT(" ")); - lookup(wxT(" ")); + lookup(" "); + lookup(" "); + lookup(" "); + lookup(" "); spellCallsign(m_callsign); - - lookup(wxT(" ")); - lookup(wxT(" ")); - lookup(wxT(" ")); - lookup(wxT(" ")); + + lookup(" "); + lookup(" "); + lookup(" "); + lookup(" "); } } @@ -179,7 +191,10 @@ void CBeaconUnit::clock() if (!m_sending) return; - unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS; + // Calculate how many frames should have been sent by now based on elapsed + // wall-clock time, then dispatch any that haven't been sent yet. This + // paces output at exactly the D-Star air rate without sleeping. + unsigned int needed = std::chrono::duration_cast(std::chrono::steady_clock::now() - m_time).count() / DSTAR_FRAME_TIME_MS; while (m_out < needed) { m_handler->transmitBeaconData(m_data + m_out * DV_FRAME_LENGTH_BYTES, DV_FRAME_LENGTH_BYTES, false); @@ -195,14 +210,16 @@ void CBeaconUnit::clock() } } -bool CBeaconUnit::lookup(const wxString &id) +bool CBeaconUnit::lookup(const std::string& id) { - CIndexRecord* info = m_index[id]; - if (info == NULL) { - // wxLogError(wxT("Cannot find the AMBE index for *%s*"), id.c_str()); + CIndexList_t::iterator it = m_index.find(id); + if (it == m_index.end() || it->second == nullptr) { + // Cannot find the AMBE index for this id return false; } + CIndexRecord* info = it->second; + unsigned int start = info->getStart(); unsigned int length = info->getLength(); @@ -232,82 +249,81 @@ bool CBeaconUnit::lookup(const wxString &id) return true; } -void CBeaconUnit::spellCallsign(const wxString &callsign) +void CBeaconUnit::spellCallsign(const std::string& callsign) { - unsigned int length = callsign.Len(); + unsigned int length = callsign.length(); for (unsigned int i = 0U; i < (length - 1U); i++) { - wxString c = callsign.Mid(i, 1U); + std::string c = callsign.substr(i, 1U); - if (!c.IsSameAs(wxT(" "))) + if (c != " ") lookup(c); } - wxChar c = callsign.GetChar(length - 1U); + char c = callsign[length - 1U]; switch (c) { - case wxT('A'): - lookup(wxT("alpha")); + case 'A': + lookup("alpha"); break; - case wxT('B'): - lookup(wxT("bravo")); + case 'B': + lookup("bravo"); break; - case wxT('C'): - lookup(wxT("charlie")); + case 'C': + lookup("charlie"); break; - case wxT('D'): - lookup(wxT("delta")); + case 'D': + lookup("delta"); break; default: - lookup(wxString(c)); + lookup(std::string(1, c)); break; } } -bool CBeaconUnit::readAMBE(const wxString& name) +bool CBeaconUnit::readAMBE(const std::string& name) { - wxFileName fileName(wxFileName::GetHomeDir(), name); - - if (!fileName.IsFileReadable()) { - wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); -#if defined(__WINDOWS__) - fileName.Assign(::wxGetCwd(), name); +#if defined(_WIN32) + const char* home = getenv("USERPROFILE"); #else - fileName.Assign(wxT(DATA_DIR), name); + const char* home = getenv("HOME"); #endif - if (!fileName.IsFileReadable()) { - wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + std::string homePath = std::string(home != nullptr ? home : "") + "/" + name; + std::string filePath; + + if (access(homePath.c_str(), R_OK) == 0) { + filePath = homePath; + } else { + std::string dataPath = std::string(DATA_DIR) + "/" + name; + if (access(dataPath.c_str(), R_OK) == 0) { + filePath = dataPath; + } else { return false; } } - wxFFile file; - - bool ret = file.Open(fileName.GetFullPath().c_str(), wxT("rb")); - if (!ret) { - wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str()); + FILE* file = ::fopen(filePath.c_str(), "rb"); + if (file == nullptr) return false; - } - - wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str()); unsigned char buffer[VOICE_FRAME_LENGTH_BYTES]; - size_t n = file.Read(buffer, 4U); + size_t n = ::fread(buffer, 1U, 4U, file); if (n != 4U) { - wxLogMessage(wxT("Unable to read the header from %s"), fileName.GetFullPath().c_str()); - file.Close(); + ::fclose(file); return false; } if (::memcmp(buffer, "AMBE", 4U) != 0) { - wxLogMessage(wxT("Invalid header from %s"), fileName.GetFullPath().c_str()); - file.Close(); + ::fclose(file); return false; } - // Length of the file minus the header - unsigned int length = file.Length() - 4U; + // Determine file length minus the 4-byte header + ::fseek(file, 0L, SEEK_END); + long fileSize = ::ftell(file); + ::fseek(file, 4L, SEEK_SET); + unsigned int length = (unsigned int)(fileSize - 4L); // Hold the file data plus silence at the end m_ambe = new unsigned char[length + SILENCE_LENGTH * VOICE_FRAME_LENGTH_BYTES]; @@ -318,77 +334,68 @@ bool CBeaconUnit::readAMBE(const wxString& name) for (unsigned int i = 0U; i < SILENCE_LENGTH; i++, p += VOICE_FRAME_LENGTH_BYTES) ::memcpy(p, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); - n = file.Read(p, length); + n = ::fread(p, 1U, length, file); if (n != length) { - wxLogMessage(wxT("Unable to read the AMBE data from %s"), fileName.GetFullPath().c_str()); - file.Close(); + ::fclose(file); delete[] m_ambe; - m_ambe = NULL; + m_ambe = nullptr; return false; } - file.Close(); + ::fclose(file); return true; } -bool CBeaconUnit::readIndex(const wxString& name) +bool CBeaconUnit::readIndex(const std::string& name) { - wxFileName fileName(wxFileName::GetHomeDir(), name); - - if (!fileName.IsFileReadable()) { - wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); -#if defined(__WINDOWS__) - fileName.Assign(::wxGetCwd(), name); +#if defined(_WIN32) + const char* home = getenv("USERPROFILE"); #else - fileName.Assign(wxT(DATA_DIR), name); + const char* home = getenv("HOME"); #endif - if (!fileName.IsFileReadable()) { - wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + std::string homePath = std::string(home != nullptr ? home : "") + "/" + name; + std::string filePath; + + if (access(homePath.c_str(), R_OK) == 0) { + filePath = homePath; + } else { + std::string dataPath = std::string(DATA_DIR) + "/" + name; + if (access(dataPath.c_str(), R_OK) == 0) { + filePath = dataPath; + } else { return false; } } - wxTextFile file; - - bool ret = file.Open(fileName.GetFullPath()); - if (!ret) { - wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str()); + std::ifstream file(filePath); + if (!file.is_open()) return false; - } // Add a silence entry at the beginning - m_index[wxT(" ")] = new CIndexRecord(wxT(" "), 0U, SILENCE_LENGTH); - - wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str()); - - unsigned int nLines = file.GetLineCount(); - - for (unsigned int i = 0; i < nLines; i++) { - wxString line = file.GetLine(i); - - if (line.length() > 0 && line.GetChar(0) != wxT('#')) { - wxStringTokenizer t(line, wxT(" \t\r\n"), wxTOKEN_STRTOK); - wxString name = t.GetNextToken(); - wxString startTxt = t.GetNextToken(); - wxString lengthTxt = t.GetNextToken(); - - if (!name.IsEmpty() && !startTxt.IsEmpty() && !lengthTxt.IsEmpty()) { - unsigned long start; - startTxt.ToULong(&start); - - unsigned long length; - lengthTxt.ToULong(&length); - - if (start >= m_ambeLength || (start + length) >= m_ambeLength) - wxLogError(wxT("The start or end for *%s* is out of range, start: %lu, end: %lu"), name.c_str(), start, start + length); - else - m_index[name] = new CIndexRecord(name, start + SILENCE_LENGTH, length); + m_index[" "] = new CIndexRecord(" ", 0U, SILENCE_LENGTH); + + std::string line; + while (std::getline(file, line)) { + if (!line.empty() && line[0] != '#') { + std::istringstream iss(line); + std::string entryName, startTxt, lengthTxt; + iss >> entryName >> startTxt >> lengthTxt; + + if (!entryName.empty() && !startTxt.empty() && !lengthTxt.empty()) { + unsigned long start = std::stoul(startTxt); + unsigned long length = std::stoul(lengthTxt); + + if (start >= m_ambeLength || (start + length) >= m_ambeLength) { + // Out of range entry — skip silently + } else { + m_index[entryName] = new CIndexRecord(entryName, start + SILENCE_LENGTH, length); + } } } } - file.Close(); + file.close(); return true; } diff --git a/Common/BeaconUnit.h b/Common/BeaconUnit.h index 84f3cd5..fa4e129 100644 --- a/Common/BeaconUnit.h +++ b/Common/BeaconUnit.h @@ -23,18 +23,25 @@ #include "BeaconCallback.h" #include "DStarDefines.h" -#include +#include "StdCompat.h" +#include +#include +/* + * Maps a word or single character (e.g. "alpha", "B", " ") to a slice of the + * pre-recorded AMBE audio buffer. The index file (.indx) contains one entry + * per line: . + */ class CIndexRecord { public: - CIndexRecord(const wxString& name, unsigned int start, unsigned int length) : + CIndexRecord(const std::string& name, unsigned int start, unsigned int length) : m_name(name), m_start(start), m_length(length) { } - wxString getName() const + std::string getName() const { return m_name; } @@ -50,43 +57,65 @@ public: } private: - wxString m_name; - unsigned int m_start; - unsigned int m_length; + std::string m_name; + unsigned int m_start; // Frame offset into the AMBE buffer. + unsigned int m_length; // Number of AMBE frames for this word/character. }; -WX_DECLARE_STRING_HASH_MAP(CIndexRecord*, CIndexList_t); +typedef std::unordered_map CIndexList_t; +/* + * Generates a voice ID beacon by assembling pre-recorded AMBE audio frames. + * + * On construction the .ambe file (raw VOICE_FRAME_LENGTH_BYTES chunks) and + * its companion .indx file are loaded for the configured language. When + * sendBeacon() is called, spellCallsign() looks up each character of the + * callsign in the index and copies the corresponding AMBE frames into an + * internal playback buffer. The last character is spoken phonetically + * (e.g. "A" -> "alpha") to follow amateur radio convention. + * + * clock() is called every DSTAR_FRAME_TIME_MS (20 ms) by the repeater thread. + * It compares elapsed wall-clock time against m_out (frames dispatched) to + * pace frame delivery to the transmitter without requiring a dedicated thread. + * + * If no voice files are available, a silent beacon (null AMBE frames) with + * slow-data text is still transmitted. + */ class CBeaconUnit { public: - CBeaconUnit(IBeaconCallback* handler, const wxString& callsign, const wxString& text, bool voice, TEXT_LANG language); + CBeaconUnit(IBeaconCallback* handler, const std::string& callsign, const std::string& text, bool voice, TEXT_LANG language); ~CBeaconUnit(); + // Assembles the beacon frames and signals the repeater thread to begin TX. void sendBeacon(); + // Called every repeater tick; releases frames to the transmitter at air rate. void clock(); private: - unsigned char* m_ambe; - unsigned int m_ambeLength; - unsigned char* m_data; - unsigned int m_dataLength; - CIndexList_t m_index; + unsigned char* m_ambe; // Raw AMBE frames loaded from the .ambe file. + unsigned int m_ambeLength; // Total frames available in m_ambe. + unsigned char* m_data; // Assembled DV frames ready for transmission. + unsigned int m_dataLength; // Bytes written into m_data so far. + CIndexList_t m_index; // Word/character -> AMBE frame mapping. TEXT_LANG m_language; IBeaconCallback* m_handler; - wxString m_callsign; - CSlowDataEncoder m_encoder; - unsigned int m_in; - unsigned int m_out; - unsigned int m_seqNo; - wxStopWatch m_time; + std::string m_callsign; + CSlowDataEncoder m_encoder; // Encodes the beacon text into slow-data fields. + unsigned int m_in; // Total frames queued into m_data. + unsigned int m_out; // Frames dispatched to the transmitter so far. + unsigned int m_seqNo; // Slow-data sequence counter (0–20). + std::chrono::steady_clock::time_point m_time; // Wall-clock start time of the beacon. bool m_sending; - bool lookup(const wxString& id); - void spellCallsign(const wxString& callsign); + // Appends AMBE frames for a word/character from the index into m_data. + bool lookup(const std::string& id); + // Spells out the callsign character by character; the last character uses + // the phonetic alphabet (A→alpha, B→bravo, C→charlie, D→delta). + void spellCallsign(const std::string& callsign); - bool readAMBE(const wxString& name); - bool readIndex(const wxString& name); + bool readAMBE(const std::string& name); + bool readIndex(const std::string& name); }; #endif diff --git a/Common/CCITTChecksum.cpp b/Common/CCITTChecksum.cpp index 62baf10..ae84264 100644 --- a/Common/CCITTChecksum.cpp +++ b/Common/CCITTChecksum.cpp @@ -15,6 +15,9 @@ #include "Utils.h" +#include +#include + static const unsigned short ccittTab[] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, @@ -60,15 +63,15 @@ CCCITTChecksum::~CCCITTChecksum() void CCCITTChecksum::update(const unsigned char* data, unsigned int length) { - wxASSERT(data != NULL); + assert(data != nullptr); for (unsigned int i = 0U; i < length; i++) - m_crc16 = (wxUint16(m_crc8[0U]) << 8) ^ ccittTab[m_crc8[1U] ^ data[i]]; + m_crc16 = (uint16_t(m_crc8[0U]) << 8) ^ ccittTab[m_crc8[1U] ^ data[i]]; } void CCCITTChecksum::result(unsigned char* data) { - wxASSERT(data != NULL); + assert(data != nullptr); data[0U] = m_crc8[1U]; data[1U] = m_crc8[0U]; @@ -76,7 +79,7 @@ void CCCITTChecksum::result(unsigned char* data) bool CCCITTChecksum::check(const unsigned char* data) { - wxASSERT(data != NULL); + assert(data != nullptr); unsigned char sum[2U]; result(sum); diff --git a/Common/CCITTChecksum.h b/Common/CCITTChecksum.h index 3e202cd..4d24b1d 100644 --- a/Common/CCITTChecksum.h +++ b/Common/CCITTChecksum.h @@ -14,8 +14,18 @@ #ifndef CCITTChecksum_H #define CCITTChecksum_H -#include +#include +/* + * CCITT-16 (CRC-16/CCITT) checksum in standard bit order. + * + * update() feeds bytes into the running CRC. result() writes the two-byte + * checksum into the provided buffer. check() compares a stored two-byte + * checksum against the computed value. reset() resets to the initial state. + * + * See also CCCITTChecksumReverse, which uses a bit-reversed variant required + * by the D-Star radio header and DSRP wire formats. + */ class CCCITTChecksum { public: CCCITTChecksum(); @@ -31,8 +41,8 @@ public: private: union { - wxUint16 m_crc16; - wxUint8 m_crc8[2U]; + uint16_t m_crc16; + uint8_t m_crc8[2U]; }; }; diff --git a/Common/CCITTChecksumReverse.cpp b/Common/CCITTChecksumReverse.cpp index 076d164..474225c 100644 --- a/Common/CCITTChecksumReverse.cpp +++ b/Common/CCITTChecksumReverse.cpp @@ -15,6 +15,9 @@ #include "Utils.h" +#include +#include + static const unsigned short ccittTab[] = { 0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf, 0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7, @@ -60,15 +63,15 @@ CCCITTChecksumReverse::~CCCITTChecksumReverse() void CCCITTChecksumReverse::update(const unsigned char* data, unsigned int length) { - wxASSERT(data != NULL); + assert(data != nullptr); for (unsigned int i = 0U; i < length; i++) - m_crc16 = wxUint16(m_crc8[1U]) ^ ccittTab[m_crc8[0U] ^ data[i]]; + m_crc16 = uint16_t(m_crc8[1U]) ^ ccittTab[m_crc8[0U] ^ data[i]]; } void CCCITTChecksumReverse::result(unsigned char* data) { - wxASSERT(data != NULL); + assert(data != nullptr); m_crc16 = ~m_crc16; @@ -78,7 +81,7 @@ void CCCITTChecksumReverse::result(unsigned char* data) bool CCCITTChecksumReverse::check(const unsigned char* data) { - wxASSERT(data != NULL); + assert(data != nullptr); unsigned char sum[2U]; result(sum); diff --git a/Common/CCITTChecksumReverse.h b/Common/CCITTChecksumReverse.h index febba08..52631b1 100644 --- a/Common/CCITTChecksumReverse.h +++ b/Common/CCITTChecksumReverse.h @@ -14,8 +14,17 @@ #ifndef CCITTChecksumReverse_H #define CCITTChecksumReverse_H -#include +#include +/* + * CCITT-16 checksum with reversed bit order, as required by the D-Star + * radio header format and the DSRP network protocol. + * + * The API is identical to CCCITTChecksum; only the polynomial bit ordering + * differs. This variant is used whenever a checksum is appended to or + * verified in a D-Star header (CHeaderData, DSRP header packets, .dvtool + * file headers, slow-data header blocks). + */ class CCCITTChecksumReverse { public: CCCITTChecksumReverse(); @@ -31,8 +40,8 @@ public: private: union { - wxUint16 m_crc16; - wxUint8 m_crc8[2U]; + uint16_t m_crc16; + uint8_t m_crc8[2U]; }; }; diff --git a/Common/CallsignList.cpp b/Common/CallsignList.cpp index f55a1f5..23a4264 100644 --- a/Common/CallsignList.cpp +++ b/Common/CallsignList.cpp @@ -19,9 +19,10 @@ #include "CallsignList.h" #include "DStarDefines.h" -#include +#include +#include -CCallsignList::CCallsignList(const wxString& filename) : +CCallsignList::CCallsignList(const std::string& filename) : m_filename(filename), m_callsigns() { @@ -34,43 +35,41 @@ CCallsignList::~CCallsignList() bool CCallsignList::load() { - wxTextFile file; - - bool res = file.Open(m_filename); - if (!res) + std::ifstream file(m_filename); + if (!file.is_open()) return false; - unsigned int lines = file.GetLineCount(); - if (lines == 0U) { - file.Close(); - return true; - } - - m_callsigns.Alloc(lines); + std::string callsign; + while (std::getline(file, callsign)) { + // Strip trailing CR so CRLF files (Windows line endings) are handled correctly + if (!callsign.empty() && callsign.back() == '\r') + callsign.pop_back(); - wxString callsign = file.GetFirstLine(); + // Convert to uppercase + std::transform(callsign.begin(), callsign.end(), callsign.begin(), ::toupper); - while (!file.Eof()) { - callsign.MakeUpper(); - callsign.Append(wxT(" ")); - callsign.Truncate(LONG_CALLSIGN_LENGTH); + // Pad to LONG_CALLSIGN_LENGTH with spaces, then truncate + callsign.append(8U, ' '); + callsign.resize(LONG_CALLSIGN_LENGTH); - m_callsigns.Add(callsign); - - callsign = file.GetNextLine(); + m_callsigns.push_back(callsign); } - file.Close(); + file.close(); return true; } unsigned int CCallsignList::getCount() const { - return m_callsigns.GetCount(); + return m_callsigns.size(); } -bool CCallsignList::isInList(const wxString& callsign) const +bool CCallsignList::isInList(const std::string& callsign) const { - return m_callsigns.Index(callsign) != wxNOT_FOUND; + for (const auto& cs : m_callsigns) { + if (cs == callsign) + return true; + } + return false; } diff --git a/Common/CallsignList.h b/Common/CallsignList.h index 2a8d06c..38030f7 100644 --- a/Common/CallsignList.h +++ b/Common/CallsignList.h @@ -19,22 +19,32 @@ #ifndef CallsignList_H #define CallsignList_H -#include +#include "StdCompat.h" +/* + * Loads a callsign access list (whitelist, blacklist, or greylist) from a + * plain-text file, one callsign per line. + * + * Each entry is normalised on load: converted to uppercase and padded/truncated + * to exactly LONG_CALLSIGN_LENGTH (8) characters with trailing spaces. This + * matches the fixed-width format used in D-Star radio headers, so isInList() + * can do a simple string comparison without any normalisation at call time. + */ class CCallsignList { public: - CCallsignList(const wxString& filename); + CCallsignList(const std::string& filename); ~CCallsignList(); bool load(); unsigned int getCount() const; - bool isInList(const wxString& callsign) const; + // Returns true if callsign (already padded to 8 chars) is in the list. + bool isInList(const std::string& callsign) const; private: - wxString m_filename; - wxArrayString m_callsigns; + std::string m_filename; + std::vector m_callsigns; }; #endif diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj deleted file mode 100644 index d8f2373..0000000 --- a/Common/Common.vcxproj +++ /dev/null @@ -1,266 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {3753EF20-2330-415E-B933-2560A474498B} - Common - Win32Proj - 10.0 - - - - StaticLibrary - v142 - Unicode - true - - - StaticLibrary - v142 - Unicode - true - - - StaticLibrary - v142 - Unicode - - - StaticLibrary - v142 - Unicode - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>14.0.24720.0 - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - $(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswud;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include - - - $(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_x64_dll\mswud;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - $(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswu;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include - - - $(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_x64_dll\mswu;$(SolutionDir)..\libusb-win32-bin-1.2.6.0\include - - - - - - - Disabled - WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) - false - true - EnableFastChecks - MultiThreadedDebugDLL - - Level4 - EditAndContinue - $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) - - - - - - - - - Disabled - WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - ProgramDatabase - $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) - - - - - - - - MaxSpeed - true - $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\HID;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - Level3 - ProgramDatabase - - - - - - - - - MaxSpeed - true - $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\HID;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters deleted file mode 100644 index cc105c8..0000000 --- a/Common/Common.vcxproj.filters +++ /dev/null @@ -1,332 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/Common/DStarDefines.h b/Common/DStarDefines.h index a21f17d..a57ab56 100644 --- a/Common/DStarDefines.h +++ b/Common/DStarDefines.h @@ -11,159 +11,239 @@ * GNU General Public License for more details. */ +/* + * Central D-Star digital voice protocol constants. These values are mandated by + * the D-Star specification and must not be changed without breaking on-air + * compatibility. They cover the physical layer (GMSK modulation), frame + * structure, slow-data encoding, flag fields, callsign formats, timing, and + * network linking ports. + */ + #ifndef DStarDefines_H #define DStarDefines_H -#include +#include "StdCompat.h" + +// --- Physical / modulation layer --- +// D-Star uses GMSK at 4800 bps with a Gaussian BT product of 0.5. const unsigned int DSTAR_GMSK_SYMBOL_RATE = 4800U; const float DSTAR_GMSK_BT = 0.5F; -const unsigned char DATA_SYNC_BYTES[] = {0x55, 0x2D, 0x16}; +// --- Frame structure --- -const unsigned char END_PATTERN_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +// Three-byte pattern that marks the start of a DV data frame within a stream. +inline const unsigned char DATA_SYNC_BYTES[] = {0x55, 0x2D, 0x16}; + +// Sent after the last DV frame to signal end-of-transmission on air. +// Only the first END_PATTERN_LENGTH_BYTES bytes are transmitted; the rest are padding. +inline const unsigned char END_PATTERN_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; const unsigned int END_PATTERN_LENGTH_BYTES = 6U; -const unsigned char NULL_AMBE_DATA_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8}; +// AMBE codec output for a frame of silence (used to fill gaps). +inline const unsigned char NULL_AMBE_DATA_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8}; +// Slow-data bytes representing an empty payload. // Note that these are already scrambled, 0x66 0x66 0x66 otherwise -const unsigned char NULL_SLOW_DATA_BYTES[] = {0x16, 0x29, 0xF5}; +inline const unsigned char NULL_SLOW_DATA_BYTES[] = {0x16, 0x29, 0xF5}; -const unsigned char NULL_FRAME_DATA_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x16, 0x29, 0xF5}; +// Combined null DV frame: NULL_AMBE_DATA_BYTES followed by NULL_SLOW_DATA_BYTES. +inline const unsigned char NULL_FRAME_DATA_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x16, 0x29, 0xF5}; +// Each DV frame carries 9 bytes of AMBE-compressed voice (72 bits at 3600 bps). const unsigned int VOICE_FRAME_LENGTH_BYTES = 9U; +// Each DV frame also carries 3 bytes of slow data (24 bits at 1200 bps). const unsigned int DATA_FRAME_LENGTH_BYTES = 3U; +// Total wire size of one DV frame: voice + slow-data interleaved. const unsigned int DV_FRAME_LENGTH_BYTES = VOICE_FRAME_LENGTH_BYTES + DATA_FRAME_LENGTH_BYTES; // The length of the end frame, three bytes extra +// Maximum DV frame buffer size, accommodating the end-of-transmission extra bytes. const unsigned int DV_FRAME_MAX_LENGTH_BYTES = DV_FRAME_LENGTH_BYTES + 3U; +// Length of the FEC (forward error correction) section in the radio header. const unsigned int FEC_SECTION_LENGTH_BYTES = 83U; +// On-air D-Star header size: 41 bytes covering callsigns, flags, and FEC. const unsigned int RADIO_HEADER_LENGTH_BYTES = 41U; +// 21 DV frames form one slow-data block (enough to carry the full header in-band). const unsigned int DATA_BLOCK_SIZE_BYTES = 21U * DV_FRAME_LENGTH_BYTES; +// --- Audio / DSP sizing --- + +// Baseband sample rate used by modem drivers that process raw I/Q or audio. const unsigned int DSTAR_RADIO_SAMPLE_RATE = 48000U; +// Samples per processing block handed to the modem (20 ms at 48 kHz). const unsigned int DSTAR_RADIO_BLOCK_SIZE = 960U; +// Samples per AMBE codec frame (20 ms at 8 kHz narrowband). const unsigned int DSTAR_AUDIO_BLOCK_SIZE = 160U; +// --- Callsign / locator field widths --- + +// Maidenhead grid locator string length (e.g. "IO91wm"). const unsigned int LOCATOR_LENGTH = 6U; +// D-Star callsign field is always 8 characters, right-padded with spaces. const unsigned int LONG_CALLSIGN_LENGTH = 8U; +// Suffix / RPT2 suffix field (e.g. " G" for gateway, " A" for port A). const unsigned int SHORT_CALLSIGN_LENGTH = 4U; +// --- Slow-data field encoding --- + +// Upper nibble of the slow-data type byte identifies the payload type. const unsigned char SLOW_DATA_TYPE_MASK = 0xF0U; -const unsigned char SLOW_DATA_TYPE_GPSDATA = 0x30U; -const unsigned char SLOW_DATA_TYPE_TEXT = 0x40U; -const unsigned char SLOW_DATA_TYPE_HEADER = 0x50U; -const unsigned char SLOW_DATA_TYPE_SQUELCH = 0xC0U; +const unsigned char SLOW_DATA_TYPE_GPSDATA = 0x30U; // APRS/GPS position string +const unsigned char SLOW_DATA_TYPE_TEXT = 0x40U; // Free-text message (TX message) +const unsigned char SLOW_DATA_TYPE_HEADER = 0x50U; // Repeated radio header +const unsigned char SLOW_DATA_TYPE_SQUELCH = 0xC0U; // Squelch-tail / keep-alive +// Lower nibble gives the number of valid data bytes in this slow-data chunk. const unsigned char SLOW_DATA_LENGTH_MASK = 0x0FU; +// Maximum length of a slow-data text message in characters. const unsigned int SLOW_DATA_TEXT_LENGTH = 20U; +// --- Scrambler --- + +// XOR pattern applied to each slow-data byte pair to decorrelate bit patterns +// on air. Applied as byte[0] ^= BYTE1, byte[1] ^= BYTE2, byte[2] ^= BYTE3. const unsigned char SCRAMBLER_BYTE1 = 0x70U; const unsigned char SCRAMBLER_BYTE2 = 0x4FU; const unsigned char SCRAMBLER_BYTE3 = 0x93U; +// --- Header flag byte 1 (RF_HEADER.flag[0]) --- + +// Set when the frame carries data rather than voice. const unsigned char DATA_MASK = 0x80U; +// Set when a repeater is involved in the QSO. const unsigned char REPEATER_MASK = 0x40U; +// Set when the transmission was interrupted (incomplete header received). const unsigned char INTERRUPTED_MASK = 0x20U; +// Set for control-channel signalling frames. const unsigned char CONTROL_SIGNAL_MASK = 0x10U; +// Set to request priority / urgent handling. const unsigned char URGENT_MASK = 0x08U; +// --- Header flag byte 1 lower nibble: repeater control codes --- + const unsigned char REPEATER_CONTROL_MASK = 0x07U; -const unsigned char REPEATER_CONTROL = 0x07U; -const unsigned char AUTO_REPLY = 0x06U; -const unsigned char RESEND_REQUESTED = 0x04U; -const unsigned char ACK_FLAG = 0x03U; -const unsigned char NO_RESPONSE = 0x02U; -const unsigned char RELAY_UNAVAILABLE = 0x01U; +const unsigned char REPEATER_CONTROL = 0x07U; // Normal repeater frame +const unsigned char AUTO_REPLY = 0x06U; // Automatic acknowledgement +const unsigned char RESEND_REQUESTED = 0x04U; // Request retransmission +const unsigned char ACK_FLAG = 0x03U; // Positive acknowledgement +const unsigned char NO_RESPONSE = 0x02U; // Destination not responding +const unsigned char RELAY_UNAVAILABLE = 0x01U; // Requested relay is busy/offline + +// --- Timing --- +// Number of 48 kHz samples per symbol at 4800 bps. const unsigned int DSTAR_RADIO_BIT_LENGTH = DSTAR_RADIO_SAMPLE_RATE / DSTAR_GMSK_SYMBOL_RATE; +// D-Star frame period: one DV frame every 20 ms = 50 frames per second. const unsigned int DSTAR_FRAME_TIME_MS = 20U; const unsigned int DSTAR_FRAMES_PER_SEC = 50U; +// Processing ticks per second derived from the DSP block size (48000/960 = 50). const unsigned int DSTAR_TICKS_PER_SEC = DSTAR_RADIO_SAMPLE_RATE / DSTAR_RADIO_BLOCK_SIZE; +// --- Network linking ports --- + +// UDP port used by the DExtra reflector linking protocol. const unsigned int DEXTRA_PORT = 30001U; +// UDP port used by the DCS reflector linking protocol. const unsigned int DCS_PORT = 30051U; +// --- Modem / protocol tuning --- + +// Byte count exchanged per USB transaction with the GMSK modem firmware. const unsigned int GMSK_MODEM_DATA_LENGTH = 8U; +// Minimum consecutive valid frames required before a radio/local/network +// transmission is considered stable and handed to the repeater logic. const unsigned int RADIO_RUN_FRAME_COUNT = 5U; const unsigned int LOCAL_RUN_FRAME_COUNT = 1U; const unsigned int NETWORK_RUN_FRAME_COUNT = 25U; -const unsigned int DSTAR_BLEEP_FREQ = 2000U; -const unsigned int DSTAR_BLEEP_LENGTH = 100U; -const float DSTAR_BLEEP_AMPL = 0.5F; +// Parameters for the end-of-transmission "bleep" courtesy tone. +const unsigned int DSTAR_BLEEP_FREQ = 2000U; // Hz +const unsigned int DSTAR_BLEEP_LENGTH = 100U; // ms +const float DSTAR_BLEEP_AMPL = 0.5F; // 0.0–1.0 amplitude +// Seconds of silence from the network gateway before treating the link as lost. const unsigned int NETWORK_TIMEOUT = 2U; -const unsigned int SPLIT_RX_GUI_COUNT = 5U; -const unsigned int SPLIT_RX_COUNT = 25U; -const unsigned int SPLIT_TX_GUI_COUNT = 3U; -const unsigned int SPLIT_TX_COUNT = 5U; +// --- Split (half-duplex split site) frame counts --- + +// How many frames to buffer on RX/TX sides in split mode. +const unsigned int SPLIT_RX_COUNT = 25U; +const unsigned int SPLIT_TX_COUNT = 5U; + +// --- Enums --- +// Repeater main state machine. Drives the TX/RX loop in the thread classes. enum DSTAR_RPT_STATE { - DSRS_SHUTDOWN, - DSRS_LISTENING, - DSRS_VALID, - DSRS_VALID_WAIT, - DSRS_INVALID, - DSRS_INVALID_WAIT, - DSRS_TIMEOUT, - DSRS_TIMEOUT_WAIT, - DSRS_NETWORK + DSRS_SHUTDOWN, // Repeater is administratively shut down + DSRS_LISTENING, // Idle, waiting for a valid D-Star header + DSRS_VALID, // Receiving a transmission from a known/allowed callsign + DSRS_VALID_WAIT, // Transmission ended; waiting for the tail to clear + DSRS_INVALID, // Receiving from an unknown or blocked callsign + DSRS_INVALID_WAIT, // Invalid transmission ended; clearing tail + DSRS_TIMEOUT, // Transmission exceeded the configured timeout + DSRS_TIMEOUT_WAIT, // Timeout tail clearing + DSRS_NETWORK // Relaying audio received from the network gateway }; +// Packet type tag returned by the network protocol handler to the repeater thread. enum NETWORK_TYPE { - NETWORK_NONE, - NETWORK_HEADER, - NETWORK_DATA, - NETWORK_TEXT, - NETWORK_TEMPTEXT, - NETWORK_STATUS1, + NETWORK_NONE, // No packet available + NETWORK_HEADER, // D-Star link header (start of a network QSO) + NETWORK_DATA, // DV frame payload + NETWORK_TEXT, // Slow-data text message from the gateway + NETWORK_TEMPTEXT, // Temporary/override text message + NETWORK_STATUS1, // ircDDB user status slots 1–5 NETWORK_STATUS2, NETWORK_STATUS3, NETWORK_STATUS4, NETWORK_STATUS5, - NETWORK_REGISTER + NETWORK_REGISTER // Gateway registration / keep-alive packet }; +// Operating mode selected in the config file; determines which thread class is +// instantiated and which modem functions are enabled. enum DSTAR_MODE { - MODE_DUPLEX, - MODE_SIMPLEX, - MODE_GATEWAY, - MODE_TXONLY, - MODE_RXONLY, - MODE_TXANDRX + MODE_DUPLEX, // Full duplex: separate RX and TX frequencies + MODE_SIMPLEX, // Simplex: share a single frequency for RX and TX + MODE_GATEWAY, // Gateway-only: no over-air receive, network traffic only + MODE_TXONLY, // Transmit only (e.g. beacon node) + MODE_RXONLY, // Receive only (e.g. logging node) + MODE_TXANDRX // Independent TX and RX paths (split site) }; +// Current state of a reflector or gateway link. enum LINK_STATUS { - LS_NONE, - LS_PENDING_IRCDDB, - LS_LINKING_LOOPBACK, + LS_NONE, // Not linked + LS_PENDING_IRCDDB, // Waiting for ircDDB callsign lookup to complete + LS_LINKING_LOOPBACK, // Handshake in progress for each protocol type: LS_LINKING_DEXTRA, LS_LINKING_DPLUS, LS_LINKING_DCS, LS_LINKING_CCS, - LS_LINKED_LOOPBACK, + LS_LINKED_LOOPBACK, // Fully linked on each protocol: LS_LINKED_DEXTRA, LS_LINKED_DPLUS, LS_LINKED_DCS, LS_LINKED_CCS }; +// What the repeater sends back to the calling station after a transmission. enum ACK_TYPE { - AT_NONE, - AT_BER, - AT_STATUS + AT_NONE, // No acknowledgement + AT_BER, // Bit-error-rate report + AT_STATUS // Repeater status message }; +// Language used when generating spoken or text acknowledgement messages. enum TEXT_LANG { TL_ENGLISH_UK, TL_DEUTSCH, @@ -178,21 +258,24 @@ enum TEXT_LANG { TL_NORSK }; +// How the modem hardware is connected to the host. enum CONNECTION_TYPE { CT_USB, CT_NETWORK }; +// Which USB backend to use for USB-connected modems. enum USB_INTERFACE { UI_LIBUSB, UI_WINUSB }; +// DVMega radio module variant; determines which band(s) are active. enum DVMEGA_VARIANT { - DVMV_MODEM, - DVMV_RADIO_2M, - DVMV_RADIO_70CM, - DVMV_RADIO_2M_70CM + DVMV_MODEM, // Modem-only (external radio) + DVMV_RADIO_2M, // Built-in 2 m radio + DVMV_RADIO_70CM, // Built-in 70 cm radio + DVMV_RADIO_2M_70CM // Dual-band 2 m / 70 cm radio }; #endif diff --git a/Common/DStarGMSKDemodulator.cpp b/Common/DStarGMSKDemodulator.cpp index 2b67897..273df59 100644 --- a/Common/DStarGMSKDemodulator.cpp +++ b/Common/DStarGMSKDemodulator.cpp @@ -14,7 +14,7 @@ #include "DStarGMSKDemodulator.h" // Generated by gaussfir(0.5, 4, 10) -const wxFloat32 COEFFS_TABLE[] = { +const float COEFFS_TABLE[] = { 0.000000000000003F, 0.000000000000065F, 0.000000000001037F, 0.000000000014448F, 0.000000000174579F, 0.000000001829471F, 0.000000016627294F, 0.000000131062698F, 0.000000895979719F, 0.000005312253663F, 0.000027316243802F, 0.000121821714020F, 0.000471183399421F, 0.001580581180127F, 0.004598383433830F, 0.011602594308899F, 0.025390226926262F, @@ -50,7 +50,7 @@ CDStarGMSKDemodulator::~CDStarGMSKDemodulator() { } -TRISTATE CDStarGMSKDemodulator::decode(wxFloat32 val) +TRISTATE CDStarGMSKDemodulator::decode(float val) { TRISTATE state = STATE_UNKNOWN; @@ -60,7 +60,7 @@ TRISTATE CDStarGMSKDemodulator::decode(wxFloat32 val) m_count++; if (m_count >= DCOFFSET_COUNT) { - m_accum /= wxFloat32(DCOFFSET_COUNT); + m_accum /= float(DCOFFSET_COUNT); m_offset = m_offset * 0.9F + m_accum * 0.1F; @@ -69,7 +69,7 @@ TRISTATE CDStarGMSKDemodulator::decode(wxFloat32 val) } } - wxFloat32 out = m_filter.process(val - m_offset); + float out = m_filter.process(val - m_offset); bool bit = out > 0.0F; @@ -112,7 +112,7 @@ void CDStarGMSKDemodulator::setInvert(bool set) void CDStarGMSKDemodulator::lock(bool on) { // Debugging only XXX - wxLogMessage(wxT("Current DC offset: %f"), m_offset); + // (wxLogMessage removed - was: "Current DC offset: %f", m_offset) m_locked = on; diff --git a/Common/DStarGMSKDemodulator.h b/Common/DStarGMSKDemodulator.h index 38ba850..2990957 100644 --- a/Common/DStarGMSKDemodulator.h +++ b/Common/DStarGMSKDemodulator.h @@ -18,29 +18,45 @@ #include "FIRFilter.h" #include "Utils.h" +/* + * GMSK demodulator for D-Star 4800 bps received audio. + * + * Processes audio samples one at a time via decode(). Internally maintains a + * software PLL to track symbol timing, and a FIR low-pass filter to remove + * out-of-band noise before bit decisions are made. + * + * Returns TRISTATE: + * STATE_TRUE — a '1' bit was clocked out at this sample + * STATE_FALSE — a '0' bit was clocked out at this sample + * STATE_UNKNOWN — mid-symbol; no bit decision yet + * + * lock() can be used to freeze PLL adaptation once a stable signal is detected, + * preventing the PLL from wandering during long runs of the same bit value. + */ class CDStarGMSKDemodulator { public: CDStarGMSKDemodulator(); ~CDStarGMSKDemodulator(); - TRISTATE decode(wxFloat32 val); + TRISTATE decode(float val); void setInvert(bool set); void reset(); + // Freezes (on=true) or enables (on=false) PLL frequency adaptation. void lock(bool on); private: - CFIRFilter m_filter; - bool m_invert; - unsigned int m_pll; - bool m_prev; - unsigned int m_inc; - bool m_locked; - wxFloat32 m_offset; - wxFloat32 m_accum; - unsigned int m_count; + CFIRFilter m_filter; + bool m_invert; + unsigned int m_pll; // Current PLL phase accumulator. + bool m_prev; // Previous bit value, used for GMSK differential decoding. + unsigned int m_inc; // PLL phase increment per sample. + bool m_locked; + float m_offset; + float m_accum; + unsigned int m_count; }; #endif diff --git a/Common/DStarGMSKModulator.cpp b/Common/DStarGMSKModulator.cpp index 37c6c0d..07a01c7 100644 --- a/Common/DStarGMSKModulator.cpp +++ b/Common/DStarGMSKModulator.cpp @@ -15,8 +15,10 @@ #include "DStarDefines.h" +#include + // Generated by gaussfir(0.5, 4, 10) -const wxFloat32 COEFFS_TABLE[] = { +const float COEFFS_TABLE[] = { 0.000000000000003F, 0.000000000000065F, 0.000000000001037F, 0.000000000014448F, 0.000000000174579F, 0.000000001829471F, 0.000000016627294F, 0.000000131062698F, 0.000000895979719F, 0.000005312253663F, 0.000027316243802F, 0.000121821714020F, 0.000471183399421F, 0.001580581180127F, 0.004598383433830F, 0.011602594308899F, 0.025390226926262F, @@ -38,10 +40,10 @@ CDStarGMSKModulator::~CDStarGMSKModulator() { } -unsigned int CDStarGMSKModulator::code(bool bit, wxFloat32* buffer, unsigned int length) +unsigned int CDStarGMSKModulator::code(bool bit, float* buffer, unsigned int length) { - wxASSERT(buffer != NULL); - wxASSERT(length == DSTAR_RADIO_BIT_LENGTH); + assert(buffer != nullptr); + assert(length == DSTAR_RADIO_BIT_LENGTH); if (m_invert) bit = !bit; diff --git a/Common/DStarGMSKModulator.h b/Common/DStarGMSKModulator.h index 1a1b401..9c94387 100644 --- a/Common/DStarGMSKModulator.h +++ b/Common/DStarGMSKModulator.h @@ -16,12 +16,21 @@ #include "FIRFilter.h" +/* + * GMSK modulator for D-Star 4800 bps transmissions. + * + * Converts a single NRZ bit into a sequence of float audio samples using a + * Gaussian FIR pulse-shaping filter (BT=0.5). The output sample buffer must + * be pre-allocated by the caller; code() returns the number of samples written. + * setInvert() reverses the polarity of the modulated signal for hardware that + * requires the opposite sense. + */ class CDStarGMSKModulator { public: CDStarGMSKModulator(); ~CDStarGMSKModulator(); - unsigned int code(bool bit, wxFloat32* buffer, unsigned int length); + unsigned int code(bool bit, float* buffer, unsigned int length); void setInvert(bool set); diff --git a/Common/DStarRepeaterConfig.cpp b/Common/DStarRepeaterConfig.cpp index 48adfef..7b26c1b 100644 --- a/Common/DStarRepeaterConfig.cpp +++ b/Common/DStarRepeaterConfig.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2011-2015,2018,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,724 +16,225 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include +/* + * INI-style configuration parser. + * + * File format: + * - [Section] headers delimit named groups of settings. + * - Lines of the form Key=Value set a parameter within the current section. + * - Lines beginning with '#' are comments and are ignored. + * - Empty lines are ignored. + * - Key lookup is case-sensitive; section names are case-sensitive. + * + * Default values are defined as DEFAULT_* constants at the top of this file. + * Any key absent from the file retains its default value silently. + */ -#include +#include +#include +#include +#include +#include #include "DStarRepeaterConfig.h" -const wxString KEY_CALLSIGN = wxT("callsign"); -const wxString KEY_GATEWAY = wxT("gateway"); -const wxString KEY_MODE = wxT("mode"); -const wxString KEY_ACK = wxT("ack"); -const wxString KEY_RESTRICTION = wxT("restriction"); -const wxString KEY_RPT1_VALIDATION = wxT("rpt1Validation"); -const wxString KEY_DTMF_BLANKING = wxT("dtmfBlanking"); -const wxString KEY_ERROR_REPLY = wxT("errorReply"); -const wxString KEY_GATEWAY_ADDRESS = wxT("gatewayAddress"); -const wxString KEY_GATEWAY_PORT = wxT("gatewayPort"); -const wxString KEY_LOCAL_ADDRESS = wxT("localAddress"); -const wxString KEY_LOCAL_PORT = wxT("localPort"); -const wxString KEY_NETWORK_NAME = wxT("networkName"); -const wxString KEY_MODEM_TYPE = wxT("modemType"); -const wxString KEY_TIMEOUT = wxT("timeout"); -const wxString KEY_ACK_TIME = wxT("ackTime"); -const wxString KEY_BEACON_TIME = wxT("beaconTime"); -const wxString KEY_BEACON_TEXT = wxT("beaconText"); -const wxString KEY_BEACON_VOICE = wxT("beaconVoice"); -const wxString KEY_LANGUAGE = wxT("language"); -const wxString KEY_ANNOUNCEMENT_ENABLED = wxT("announcementEnabled"); -const wxString KEY_ANNOUNCEMENT_TIME = wxT("announcementTime"); -const wxString KEY_ANNOUNCEMENT_RECORD_RPT1 = wxT("announcementRecordRPT1"); -const wxString KEY_ANNOUNCEMENT_RECORD_RPT2 = wxT("announcementRecordRPT2"); -const wxString KEY_ANNOUNCEMENT_DELETE_RPT1 = wxT("announcementDeleteRPT1"); -const wxString KEY_ANNOUNCEMENT_DELETE_RPT2 = wxT("announcementDeleteRPT2"); -const wxString KEY_CONTROL_ENABLED = wxT("controlEnabled"); -const wxString KEY_CONTROL_RPT1 = wxT("controlRPT1"); -const wxString KEY_CONTROL_RPT2 = wxT("controlRPT2"); -const wxString KEY_CONTROL_SHUTDOWN = wxT("controlShutdown"); -const wxString KEY_CONTROL_STARTUP = wxT("controlStartup"); -const wxString KEY_CONTROL_STATUS1 = wxT("controlStatus1"); -const wxString KEY_CONTROL_STATUS2 = wxT("controlStatus2"); -const wxString KEY_CONTROL_STATUS3 = wxT("controlStatus3"); -const wxString KEY_CONTROL_STATUS4 = wxT("controlStatus4"); -const wxString KEY_CONTROL_STATUS5 = wxT("controlStatus5"); -const wxString KEY_CONTROL_COMMAND1 = wxT("controlCommand1"); -const wxString KEY_CONTROL_COMMAND1_LINE = wxT("controlCommand1Line"); -const wxString KEY_CONTROL_COMMAND2 = wxT("controlCommand2"); -const wxString KEY_CONTROL_COMMAND2_LINE = wxT("controlCommand2Line"); -const wxString KEY_CONTROL_COMMAND3 = wxT("controlCommand3"); -const wxString KEY_CONTROL_COMMAND3_LINE = wxT("controlCommand3Line"); -const wxString KEY_CONTROL_COMMAND4 = wxT("controlCommand4"); -const wxString KEY_CONTROL_COMMAND4_LINE = wxT("controlCommand4Line"); -const wxString KEY_CONTROL_COMMAND5 = wxT("controlCommand5"); -const wxString KEY_CONTROL_COMMAND5_LINE = wxT("controlCommand5Line"); -const wxString KEY_CONTROL_COMMAND6 = wxT("controlCommand6"); -const wxString KEY_CONTROL_COMMAND6_LINE = wxT("controlCommand6Line"); -const wxString KEY_CONTROL_OUTPUT1 = wxT("controlOutput1"); -const wxString KEY_CONTROL_OUTPUT2 = wxT("controlOutput2"); -const wxString KEY_CONTROL_OUTPUT3 = wxT("controlOutput3"); -const wxString KEY_CONTROL_OUTPUT4 = wxT("controlOutput4"); -const wxString KEY_CONTROLLER_TYPE = wxT("controllerType"); -const wxString KEY_SERIAL_CONFIG = wxT("serialConfig"); -const wxString KEY_PTT_INVERT = wxT("pttInvert"); -const wxString KEY_ACTIVE_HANG_TIME = wxT("activeHangTime"); -const wxString KEY_OUTPUT1 = wxT("output1"); -const wxString KEY_OUTPUT2 = wxT("output2"); -const wxString KEY_OUTPUT3 = wxT("output3"); -const wxString KEY_OUTPUT4 = wxT("output4"); -const wxString KEY_LOGGING = wxT("logging"); -const wxString KEY_WINDOW_X = wxT("windowX"); -const wxString KEY_WINDOW_Y = wxT("windowY"); - -const wxString KEY_DVAP_PORT = wxT("dvapPort"); -const wxString KEY_DVAP_FREQUENCY = wxT("dvapFrequency"); -const wxString KEY_DVAP_POWER = wxT("dvapPower"); -const wxString KEY_DVAP_SQUELCH = wxT("dvapSquelch"); - -const wxString KEY_GMSK_INTERFACE = wxT("gmskInterfaceType"); -const wxString KEY_GMSK_ADDRESS = wxT("gmskAddress"); - -const wxString KEY_DVRPTR1_PORT = wxT("dvrptr1Port"); -const wxString KEY_DVRPTR1_RXINVERT = wxT("dvrptr1RXInvert"); -const wxString KEY_DVRPTR1_TXINVERT = wxT("dvrptr1TXInvert"); -const wxString KEY_DVRPTR1_CHANNEL = wxT("dvrptr1Channel"); -const wxString KEY_DVRPTR1_MODLEVEL = wxT("dvrptr1ModLevel"); -const wxString KEY_DVRPTR1_TXDELAY = wxT("dvrptr1TXDelay"); - -const wxString KEY_DVRPTR2_CONNECTION = wxT("dvrptr2Connection"); -const wxString KEY_DVRPTR2_USBPORT = wxT("dvrptr2USBPort"); -const wxString KEY_DVRPTR2_ADDRESS = wxT("dvrptr2Address"); -const wxString KEY_DVRPTR2_PORT = wxT("dvrptr2Port"); -const wxString KEY_DVRPTR2_TXINVERT = wxT("dvrptr2TXInvert"); -const wxString KEY_DVRPTR2_MODLEVEL = wxT("dvrptr2ModLevel"); -const wxString KEY_DVRPTR2_TXDELAY = wxT("dvrptr2TXDelay"); - -const wxString KEY_DVRPTR3_CONNECTION = wxT("dvrptr3Connection"); -const wxString KEY_DVRPTR3_USBPORT = wxT("dvrptr3USBPort"); -const wxString KEY_DVRPTR3_ADDRESS = wxT("dvrptr3Address"); -const wxString KEY_DVRPTR3_PORT = wxT("dvrptr3Port"); -const wxString KEY_DVRPTR3_TXINVERT = wxT("dvrptr3TXInvert"); -const wxString KEY_DVRPTR3_MODLEVEL = wxT("dvrptr3ModLevel"); -const wxString KEY_DVRPTR3_TXDELAY = wxT("dvrptr3TXDelay"); - -const wxString KEY_DVMEGA_PORT = wxT("dvmegaPort"); -const wxString KEY_DVMEGA_VARIANT = wxT("dvmegaVariant"); -const wxString KEY_DVMEGA_RXINVERT = wxT("dvmegaRXInvert"); -const wxString KEY_DVMEGA_TXINVERT = wxT("dvmegaTXInvert"); -const wxString KEY_DVMEGA_TXDELAY = wxT("dvmegaTXDelay"); -const wxString KEY_DVMEGA_RXFREQUENCY = wxT("dvmegaRXFrequency"); -const wxString KEY_DVMEGA_TXFREQUENCY = wxT("dvmegaTXFrequency"); -const wxString KEY_DVMEGA_POWER = wxT("dvmegaPower"); - -const wxString KEY_MMDVM_PORT = wxT("mmdvmPort"); -const wxString KEY_MMDVM_RXINVERT = wxT("mmdvmRXInvert"); -const wxString KEY_MMDVM_TXINVERT = wxT("mmdvmTXInvert"); -const wxString KEY_MMDVM_PTTINVERT = wxT("mmdvmPTTInvert"); -const wxString KEY_MMDVM_TXDELAY = wxT("mmdvmTXDelay"); -const wxString KEY_MMDVM_RXLEVEL = wxT("mmdvmRXLevel"); -const wxString KEY_MMDVM_TXLEVEL = wxT("mmdvmTXLevel"); - -const wxString KEY_SOUNDCARD_RXDEVICE = wxT("soundCardRXDevice"); -const wxString KEY_SOUNDCARD_TXDEVICE = wxT("soundCardTXDevice"); -const wxString KEY_SOUNDCARD_RXINVERT = wxT("soundCardRXInvert"); -const wxString KEY_SOUNDCARD_TXINVERT = wxT("soundCardTXInvert"); -const wxString KEY_SOUNDCARD_RXLEVEL = wxT("soundCardRXLevel"); -const wxString KEY_SOUNDCARD_TXLEVEL = wxT("soundCardTXLevel"); -const wxString KEY_SOUNDCARD_TXDELAY = wxT("soundCardTXDelay"); -const wxString KEY_SOUNDCARD_TXTAIL = wxT("soundCardTXTail"); - -const wxString KEY_SPLIT_LOCALADDRESS = wxT("splitLocalAddress"); -const wxString KEY_SPLIT_LOCALPORT = wxT("splitLocalPort"); -const wxString KEY_SPLIT_TXNAME = wxT("splitTXName"); -const wxString KEY_SPLIT_RXNAME = wxT("splitRXName"); -const wxString KEY_SPLIT_TIMEOUT = wxT("splitTimeout"); - -const wxString KEY_ICOM_PORT = wxT("icomPort"); - - -const wxString DEFAULT_CALLSIGN = wxT("GB3IN C"); -const wxString DEFAULT_GATEWAY = wxEmptyString; -const DSTAR_MODE DEFAULT_MODE = MODE_DUPLEX; -const ACK_TYPE DEFAULT_ACK = AT_BER; -const bool DEFAULT_RESTRICTION = false; -const bool DEFAULT_RPT1_VALIDATION = true; -const bool DEFAULT_DTMF_BLANKING = true; -const bool DEFAULT_ERROR_REPLY = true; -const wxString DEFAULT_GATEWAY_ADDRESS = wxT("127.0.0.1"); -const unsigned int DEFAULT_GATEWAY_PORT = 20010U; -const wxString DEFAULT_LOCAL_ADDRESS = wxT("127.0.0.1"); -const unsigned int DEFAULT_LOCAL_PORT = 20011U; -const wxString DEFAULT_NETWORK_NAME = wxEmptyString; -const wxString DEFAULT_MODEM_TYPE = wxT("DVAP"); -const unsigned int DEFAULT_TIMEOUT = 180U; -const unsigned int DEFAULT_ACK_TIME = 500U; -const unsigned int DEFAULT_BEACON_TIME = 600U; -const wxString DEFAULT_BEACON_TEXT = wxT("D-Star Repeater"); -const bool DEFAULT_BEACON_VOICE = false; -const TEXT_LANG DEFAULT_LANGUAGE = TL_ENGLISH_UK; -const bool DEFAULT_ANNOUNCEMENT_ENABLED = false; -const unsigned int DEFAULT_ANNOUNCEMENT_TIME = 500U; -const wxString DEFAULT_ANNOUNCEMENT_RECORD_RPT1 = wxEmptyString; -const wxString DEFAULT_ANNOUNCEMENT_RECORD_RPT2 = wxEmptyString; -const wxString DEFAULT_ANNOUNCEMENT_DELETE_RPT1 = wxEmptyString; -const wxString DEFAULT_ANNOUNCEMENT_DELETE_RPT2 = wxEmptyString; -const bool DEFAULT_CONTROL_ENABLED = false; -const wxString DEFAULT_CONTROL_RPT1 = wxEmptyString; -const wxString DEFAULT_CONTROL_RPT2 = wxEmptyString; -const wxString DEFAULT_CONTROL_SHUTDOWN = wxEmptyString; -const wxString DEFAULT_CONTROL_STARTUP = wxEmptyString; -const wxString DEFAULT_CONTROL_STATUS = wxEmptyString; -const wxString DEFAULT_CONTROL_COMMAND = wxEmptyString; -const wxString DEFAULT_CONTROL_COMMAND_LINE = wxEmptyString; -const wxString DEFAULT_CONTROL_OUTPUT = wxEmptyString; -const wxString DEFAULT_CONTROLLER_TYPE = wxEmptyString; -const unsigned int DEFAULT_SERIAL_CONFIG = 1U; -const bool DEFAULT_PTT_INVERT = false; -const unsigned int DEFAULT_ACTIVE_HANG_TIME = 0U; -const bool DEFAULT_OUTPUT = false; -const bool DEFAULT_LOGGING = false; -const int DEFAULT_WINDOW_X = -1; -const int DEFAULT_WINDOW_Y = -1; - -const wxString DEFAULT_DVAP_PORT = wxEmptyString; -const unsigned int DEFAULT_DVAP_FREQUENCY = 145500000U; -const int DEFAULT_DVAP_POWER = 10; -const int DEFAULT_DVAP_SQUELCH = -100; - -#if defined(WIN32) -const USB_INTERFACE DEFAULT_GMSK_INTERFACE = UI_WINUSB; -#else -const USB_INTERFACE DEFAULT_GMSK_INTERFACE = UI_LIBUSB; -#endif -const unsigned int DEFAULT_GMSK_ADDRESS = 0x0300U; - -const wxString DEFAULT_DVRPTR1_PORT = wxEmptyString; -const bool DEFAULT_DVRPTR1_RXINVERT = false; -const bool DEFAULT_DVRPTR1_TXINVERT = false; -const bool DEFAULT_DVRPTR1_CHANNEL = false; -const unsigned int DEFAULT_DVRPTR1_MODLEVEL = 20U; -const unsigned int DEFAULT_DVRPTR1_TXDELAY = 150U; - -const CONNECTION_TYPE DEFAULT_DVRPTR2_CONNECTION = CT_USB; -const wxString DEFAULT_DVRPTR2_USBPORT = wxEmptyString; -const wxString DEFAULT_DVRPTR2_ADDRESS = wxT("127.0.0.1"); -const unsigned int DEFAULT_DVRPTR2_PORT = 0U; -const bool DEFAULT_DVRPTR2_TXINVERT = false; -const unsigned int DEFAULT_DVRPTR2_MODLEVEL = 20U; -const unsigned int DEFAULT_DVRPTR2_TXDELAY = 150U; - -const CONNECTION_TYPE DEFAULT_DVRPTR3_CONNECTION = CT_USB; -const wxString DEFAULT_DVRPTR3_USBPORT = wxEmptyString; -const wxString DEFAULT_DVRPTR3_ADDRESS = wxT("127.0.0.1"); -const unsigned int DEFAULT_DVRPTR3_PORT = 0U; -const bool DEFAULT_DVRPTR3_TXINVERT = false; -const unsigned int DEFAULT_DVRPTR3_MODLEVEL = 20U; -const unsigned int DEFAULT_DVRPTR3_TXDELAY = 150U; - -const wxString DEFAULT_DVMEGA_PORT = wxEmptyString; -const DVMEGA_VARIANT DEFAULT_DVMEGA_VARIANT = DVMV_MODEM; -const bool DEFAULT_DVMEGA_RXINVERT = false; -const bool DEFAULT_DVMEGA_TXINVERT = false; -const unsigned int DEFAULT_DVMEGA_TXDELAY = 150U; -const unsigned int DEFAULT_DVMEGA_RXFREQUENCY = 145500000U; -const unsigned int DEFAULT_DVMEGA_TXFREQUENCY = 145500000U; -const unsigned int DEFAULT_DVMEGA_POWER = 100U; - -const wxString DEFAULT_MMDVM_PORT = wxEmptyString; -const bool DEFAULT_MMDVM_RXINVERT = false; -const bool DEFAULT_MMDVM_TXINVERT = false; -const bool DEFAULT_MMDVM_PTTINVERT = false; -const unsigned int DEFAULT_MMDVM_TXDELAY = 50U; -const unsigned int DEFAULT_MMDVM_RXLEVEL = 100U; -const unsigned int DEFAULT_MMDVM_TXLEVEL = 100U; - -const wxString DEFAULT_SOUNDCARD_RXDEVICE = wxEmptyString; -const wxString DEFAULT_SOUNDCARD_TXDEVICE = wxEmptyString; -const bool DEFAULT_SOUNDCARD_RXINVERT = false; -const bool DEFAULT_SOUNDCARD_TXINVERT = false; -const wxFloat32 DEFAULT_SOUNDCARD_RXLEVEL = 1.0F; -const wxFloat32 DEFAULT_SOUNDCARD_TXLEVEL = 1.0F; -const unsigned int DEFAULT_SOUNDCARD_TXDELAY = 150U; -const unsigned int DEFAULT_SOUNDCARD_TXTAIL = 50U; - -const wxString DEFAULT_SPLIT_LOCALADDRESS = wxEmptyString; -const unsigned int DEFAULT_SPLIT_LOCALPORT = 0U; -const wxString DEFAULT_SPLIT_TXNAME = wxEmptyString; -const wxString DEFAULT_SPLIT_RXNAME = wxEmptyString; -const unsigned int DEFAULT_SPLIT_TIMEOUT = 0U; - -const wxString DEFAULT_ICOM_PORT = wxEmptyString; - +// --------------------------------------------------------------------------- +// Section names +// --------------------------------------------------------------------------- + +static const std::string SECTION_GENERAL = "General"; +static const std::string SECTION_LOG = "Log"; +static const std::string SECTION_PATHS = "Paths"; +static const std::string SECTION_NETWORK = "Network"; +static const std::string SECTION_MODEM = "Modem"; +static const std::string SECTION_TIMES = "Times"; +static const std::string SECTION_BEACON = "Beacon"; +static const std::string SECTION_ANNOUNCEMENT = "Announcement"; +static const std::string SECTION_CONTROL = "Control"; +static const std::string SECTION_CONTROLLER = "Controller"; +static const std::string SECTION_OUTPUTS = "Outputs"; +static const std::string SECTION_FRAMELOGGING = "Frame Logging"; +static const std::string SECTION_WHITELIST = "Whitelist"; +static const std::string SECTION_BLACKLIST = "Blacklist"; +static const std::string SECTION_GREYLIST = "Greylist"; +static const std::string SECTION_DVAP = "DVAP"; +static const std::string SECTION_GMSK = "GMSK"; +static const std::string SECTION_DVRPTR1 = "DV-RPTR V1"; +static const std::string SECTION_DVRPTR2 = "DV-RPTR V2"; +static const std::string SECTION_DVRPTR3 = "DV-RPTR V3"; +static const std::string SECTION_DVMEGA = "DVMEGA"; +static const std::string SECTION_MMDVM = "MMDVM"; +static const std::string SECTION_SOUNDCARD = "Sound Card"; +static const std::string SECTION_SPLIT = "Split"; +static const std::string SECTION_ICOM = "Icom"; #if defined(MQTT) -const wxString KEY_MQTT_HOST = wxT("mqttHost"); -const wxString KEY_MQTT_PORT = wxT("mqttPort"); -const wxString KEY_MQTT_AUTH = wxT("mqttAuth"); -const wxString KEY_MQTT_USERNAME = wxT("mqttUsername"); -const wxString KEY_MQTT_PASSWORD = wxT("mqttPassword"); -const wxString KEY_MQTT_KEEPALIVE = wxT("mqttKeepalive"); -const wxString KEY_MQTT_NAME = wxT("mqttName"); - -const wxString DEFAULT_MQTT_HOST = wxT("127.0.0.1"); -const unsigned int DEFAULT_MQTT_PORT = 1883U; -const bool DEFAULT_MQTT_AUTH = false; -const wxString DEFAULT_MQTT_USERNAME = wxEmptyString; -const wxString DEFAULT_MQTT_PASSWORD = wxEmptyString; -const unsigned int DEFAULT_MQTT_KEEPALIVE = 60U; -const wxString DEFAULT_MQTT_NAME = wxT("dstar-repeater"); +static const std::string SECTION_MQTT = "MQTT"; #endif -#if defined(__WINDOWS__) +// --------------------------------------------------------------------------- +// Default values +// --------------------------------------------------------------------------- + +// [General] +static const std::string DEFAULT_CALLSIGN = "GB3IN C"; +static const std::string DEFAULT_GATEWAY = std::string(); +static const DSTAR_MODE DEFAULT_MODE = MODE_DUPLEX; +static const ACK_TYPE DEFAULT_ACK = AT_BER; +static const bool DEFAULT_RESTRICTION = false; +static const bool DEFAULT_RPT1_VALIDATION = true; +static const bool DEFAULT_DTMF_BLANKING = true; +static const bool DEFAULT_ERROR_REPLY = true; + +// [Log] +static const std::string DEFAULT_LOG_FILE_PATH = "/var/log"; +static const unsigned int DEFAULT_LOG_FILE_LEVEL = 2U; // LOG_MESSAGE +static const unsigned int DEFAULT_LOG_DISPLAY_LEVEL = 2U; // LOG_MESSAGE +static const unsigned int DEFAULT_LOG_MQTT_LEVEL = 2U; // LOG_MESSAGE + +// [Paths] +static const std::string DEFAULT_DATA_DIR = "/usr/share/dstarrepeater"; +static const std::string DEFAULT_AUDIO_DIR = "/var/log"; + +// [Network] +static const std::string DEFAULT_GATEWAY_ADDRESS = "127.0.0.1"; +static const unsigned int DEFAULT_GATEWAY_PORT = 20010U; +static const std::string DEFAULT_LOCAL_ADDRESS = "127.0.0.1"; +static const unsigned int DEFAULT_LOCAL_PORT = 20011U; +static const std::string DEFAULT_NETWORK_NAME = std::string(); + +// [Modem] +static const std::string DEFAULT_MODEM_TYPE = "DVAP"; + +// [Times] +static const unsigned int DEFAULT_TIMEOUT = 180U; +static const unsigned int DEFAULT_ACK_TIME = 500U; + +// [Beacon] +static const unsigned int DEFAULT_BEACON_TIME = 600U; +static const std::string DEFAULT_BEACON_TEXT = "D-Star Repeater"; +static const bool DEFAULT_BEACON_VOICE = false; +static const TEXT_LANG DEFAULT_LANGUAGE = TL_ENGLISH_UK; + +// [Announcement] +static const bool DEFAULT_ANNOUNCEMENT_ENABLED = false; +static const unsigned int DEFAULT_ANNOUNCEMENT_TIME = 500U; +static const std::string DEFAULT_ANNOUNCEMENT_RECORD_RPT1 = std::string(); +static const std::string DEFAULT_ANNOUNCEMENT_RECORD_RPT2 = std::string(); +static const std::string DEFAULT_ANNOUNCEMENT_DELETE_RPT1 = std::string(); +static const std::string DEFAULT_ANNOUNCEMENT_DELETE_RPT2 = std::string(); + +// [Control] +static const bool DEFAULT_CONTROL_ENABLED = false; +static const std::string DEFAULT_CONTROL_RPT1 = std::string(); +static const std::string DEFAULT_CONTROL_RPT2 = std::string(); +static const std::string DEFAULT_CONTROL_SHUTDOWN = std::string(); +static const std::string DEFAULT_CONTROL_STARTUP = std::string(); +static const std::string DEFAULT_CONTROL_STATUS = std::string(); +static const std::string DEFAULT_CONTROL_COMMAND = std::string(); +static const std::string DEFAULT_CONTROL_COMMAND_LINE = std::string(); +static const std::string DEFAULT_CONTROL_OUTPUT = std::string(); + +// [Controller] +static const std::string DEFAULT_CONTROLLER_TYPE = std::string(); +static const unsigned int DEFAULT_SERIAL_CONFIG = 1U; +static const bool DEFAULT_PTT_INVERT = false; +static const unsigned int DEFAULT_ACTIVE_HANG_TIME = 0U; + +// [Outputs] +static const bool DEFAULT_OUTPUT = false; + +// [Frame Logging] +static const bool DEFAULT_LOGGING = false; + +// [DVAP] +static const std::string DEFAULT_DVAP_PORT = std::string(); +static const unsigned int DEFAULT_DVAP_FREQUENCY = 145500000U; +static const int DEFAULT_DVAP_POWER = 10; +static const int DEFAULT_DVAP_SQUELCH = -100; + +// [GMSK] +static const USB_INTERFACE DEFAULT_GMSK_INTERFACE = UI_LIBUSB; +static const unsigned int DEFAULT_GMSK_ADDRESS = 0x0300U; + +// [DV-RPTR V1] +static const std::string DEFAULT_DVRPTR1_PORT = std::string(); +static const bool DEFAULT_DVRPTR1_RXINVERT = false; +static const bool DEFAULT_DVRPTR1_TXINVERT = false; +static const bool DEFAULT_DVRPTR1_CHANNEL = false; +static const unsigned int DEFAULT_DVRPTR1_MODLEVEL = 20U; +static const unsigned int DEFAULT_DVRPTR1_TXDELAY = 150U; + +// [DV-RPTR V2] +static const CONNECTION_TYPE DEFAULT_DVRPTR2_CONNECTION = CT_USB; +static const std::string DEFAULT_DVRPTR2_USBPORT = std::string(); +static const std::string DEFAULT_DVRPTR2_ADDRESS = "127.0.0.1"; +static const unsigned int DEFAULT_DVRPTR2_PORT = 0U; +static const bool DEFAULT_DVRPTR2_TXINVERT = false; +static const unsigned int DEFAULT_DVRPTR2_MODLEVEL = 20U; +static const unsigned int DEFAULT_DVRPTR2_TXDELAY = 150U; + +// [DV-RPTR V3] +static const CONNECTION_TYPE DEFAULT_DVRPTR3_CONNECTION = CT_USB; +static const std::string DEFAULT_DVRPTR3_USBPORT = std::string(); +static const std::string DEFAULT_DVRPTR3_ADDRESS = "127.0.0.1"; +static const unsigned int DEFAULT_DVRPTR3_PORT = 0U; +static const bool DEFAULT_DVRPTR3_TXINVERT = false; +static const unsigned int DEFAULT_DVRPTR3_MODLEVEL = 20U; +static const unsigned int DEFAULT_DVRPTR3_TXDELAY = 150U; + +// [DVMEGA] +static const std::string DEFAULT_DVMEGA_PORT = std::string(); +static const DVMEGA_VARIANT DEFAULT_DVMEGA_VARIANT = DVMV_MODEM; +static const bool DEFAULT_DVMEGA_RXINVERT = false; +static const bool DEFAULT_DVMEGA_TXINVERT = false; +static const unsigned int DEFAULT_DVMEGA_TXDELAY = 150U; +static const unsigned int DEFAULT_DVMEGA_RXFREQUENCY = 145500000U; +static const unsigned int DEFAULT_DVMEGA_TXFREQUENCY = 145500000U; +static const unsigned int DEFAULT_DVMEGA_POWER = 100U; + +// [MMDVM] +static const std::string DEFAULT_MMDVM_PORT = std::string(); +static const bool DEFAULT_MMDVM_RXINVERT = false; +static const bool DEFAULT_MMDVM_TXINVERT = false; +static const bool DEFAULT_MMDVM_PTTINVERT = false; +static const unsigned int DEFAULT_MMDVM_TXDELAY = 50U; +static const unsigned int DEFAULT_MMDVM_RXLEVEL = 100U; +static const unsigned int DEFAULT_MMDVM_TXLEVEL = 100U; + +// [Sound Card] +static const std::string DEFAULT_SOUNDCARD_RXDEVICE = std::string(); +static const std::string DEFAULT_SOUNDCARD_TXDEVICE = std::string(); +static const bool DEFAULT_SOUNDCARD_RXINVERT = false; +static const bool DEFAULT_SOUNDCARD_TXINVERT = false; +static const float DEFAULT_SOUNDCARD_RXLEVEL = 1.0F; +static const float DEFAULT_SOUNDCARD_TXLEVEL = 1.0F; +static const unsigned int DEFAULT_SOUNDCARD_TXDELAY = 150U; +static const unsigned int DEFAULT_SOUNDCARD_TXTAIL = 50U; + +// [Split] +static const std::string DEFAULT_SPLIT_LOCALADDRESS = std::string(); +static const unsigned int DEFAULT_SPLIT_LOCALPORT = 0U; +static const unsigned int DEFAULT_SPLIT_TIMEOUT = 0U; + +// [Icom] +static const std::string DEFAULT_ICOM_PORT = std::string(); -CDStarRepeaterConfig::CDStarRepeaterConfig(wxConfigBase* config, const wxString& dir, const wxString& configName, const wxString& name) : -m_config(config), -m_name(wxT("/")), -m_fileName(), -m_callsign(DEFAULT_CALLSIGN), -m_gateway(DEFAULT_GATEWAY), -m_mode(DEFAULT_MODE), -m_ack(DEFAULT_ACK), -m_restriction(DEFAULT_RESTRICTION), -m_rpt1Validation(DEFAULT_RPT1_VALIDATION), -m_dtmfBlanking(DEFAULT_DTMF_BLANKING), -m_errorReply(DEFAULT_ERROR_REPLY), -m_gatewayAddress(DEFAULT_GATEWAY_ADDRESS), -m_gatewayPort(DEFAULT_GATEWAY_PORT), -m_localAddress(DEFAULT_LOCAL_ADDRESS), -m_localPort(DEFAULT_LOCAL_PORT), -m_networkName(DEFAULT_NETWORK_NAME), -m_modemType(DEFAULT_MODEM_TYPE), -m_timeout(DEFAULT_TIMEOUT), -m_ackTime(DEFAULT_ACK_TIME), -m_beaconTime(DEFAULT_BEACON_TIME), -m_beaconText(DEFAULT_BEACON_TEXT), -m_beaconVoice(DEFAULT_BEACON_VOICE), -m_language(DEFAULT_LANGUAGE), -m_announcementEnabled(DEFAULT_ANNOUNCEMENT_ENABLED), -m_announcementTime(DEFAULT_ANNOUNCEMENT_TIME), -m_announcementRecordRPT1(DEFAULT_ANNOUNCEMENT_RECORD_RPT1), -m_announcementRecordRPT2(DEFAULT_ANNOUNCEMENT_RECORD_RPT2), -m_announcementDeleteRPT1(DEFAULT_ANNOUNCEMENT_DELETE_RPT1), -m_announcementDeleteRPT2(DEFAULT_ANNOUNCEMENT_DELETE_RPT2), -m_controlEnabled(DEFAULT_CONTROL_ENABLED), -m_controlRpt1Callsign(DEFAULT_CONTROL_RPT1), -m_controlRpt2Callsign(DEFAULT_CONTROL_RPT2), -m_controlShutdown(DEFAULT_CONTROL_SHUTDOWN), -m_controlStartup(DEFAULT_CONTROL_STARTUP), -m_controlStatus1(DEFAULT_CONTROL_STATUS), -m_controlStatus2(DEFAULT_CONTROL_STATUS), -m_controlStatus3(DEFAULT_CONTROL_STATUS), -m_controlStatus4(DEFAULT_CONTROL_STATUS), -m_controlStatus5(DEFAULT_CONTROL_STATUS), -m_controlCommand1(DEFAULT_CONTROL_COMMAND), -m_controlCommand1Line(DEFAULT_CONTROL_COMMAND_LINE), -m_controlCommand2(DEFAULT_CONTROL_COMMAND), -m_controlCommand2Line(DEFAULT_CONTROL_COMMAND_LINE), -m_controlCommand3(DEFAULT_CONTROL_COMMAND), -m_controlCommand3Line(DEFAULT_CONTROL_COMMAND_LINE), -m_controlCommand4(DEFAULT_CONTROL_COMMAND), -m_controlCommand4Line(DEFAULT_CONTROL_COMMAND_LINE), -m_controlCommand5(DEFAULT_CONTROL_COMMAND), -m_controlCommand5Line(DEFAULT_CONTROL_COMMAND_LINE), -m_controlCommand6(DEFAULT_CONTROL_COMMAND), -m_controlCommand6Line(DEFAULT_CONTROL_COMMAND_LINE), -m_controlOutput1(DEFAULT_CONTROL_OUTPUT), -m_controlOutput2(DEFAULT_CONTROL_OUTPUT), -m_controlOutput3(DEFAULT_CONTROL_OUTPUT), -m_controlOutput4(DEFAULT_CONTROL_OUTPUT), -m_controllerType(DEFAULT_CONTROLLER_TYPE), -m_serialConfig(DEFAULT_SERIAL_CONFIG), -m_pttInvert(DEFAULT_PTT_INVERT), -m_activeHangTime(DEFAULT_ACTIVE_HANG_TIME), -m_output1(DEFAULT_OUTPUT), -m_output2(DEFAULT_OUTPUT), -m_output3(DEFAULT_OUTPUT), -m_output4(DEFAULT_OUTPUT), -m_logging(DEFAULT_LOGGING), -m_x(DEFAULT_WINDOW_X), -m_y(DEFAULT_WINDOW_Y), -m_dvapPort(DEFAULT_DVAP_PORT), -m_dvapFrequency(DEFAULT_DVAP_FREQUENCY), -m_dvapPower(DEFAULT_DVAP_POWER), -m_dvapSquelch(DEFAULT_DVAP_SQUELCH), -m_gmskInterface(DEFAULT_GMSK_INTERFACE), -m_gmskAddress(DEFAULT_GMSK_ADDRESS), -m_dvrptr1Port(DEFAULT_DVRPTR1_PORT), -m_dvrptr1RXInvert(DEFAULT_DVRPTR1_RXINVERT), -m_dvrptr1TXInvert(DEFAULT_DVRPTR1_TXINVERT), -m_dvrptr1Channel(DEFAULT_DVRPTR1_CHANNEL), -m_dvrptr1ModLevel(DEFAULT_DVRPTR1_MODLEVEL), -m_dvrptr1TXDelay(DEFAULT_DVRPTR1_TXDELAY), -m_dvrptr2Connection(DEFAULT_DVRPTR2_CONNECTION), -m_dvrptr2USBPort(DEFAULT_DVRPTR2_USBPORT), -m_dvrptr2Address(DEFAULT_DVRPTR2_ADDRESS), -m_dvrptr2Port(DEFAULT_DVRPTR2_PORT), -m_dvrptr2TXInvert(DEFAULT_DVRPTR2_TXINVERT), -m_dvrptr2ModLevel(DEFAULT_DVRPTR2_MODLEVEL), -m_dvrptr2TXDelay(DEFAULT_DVRPTR2_TXDELAY), -m_dvrptr3Connection(DEFAULT_DVRPTR3_CONNECTION), -m_dvrptr3USBPort(DEFAULT_DVRPTR3_USBPORT), -m_dvrptr3Address(DEFAULT_DVRPTR3_ADDRESS), -m_dvrptr3Port(DEFAULT_DVRPTR3_PORT), -m_dvrptr3TXInvert(DEFAULT_DVRPTR3_TXINVERT), -m_dvrptr3ModLevel(DEFAULT_DVRPTR3_MODLEVEL), -m_dvrptr3TXDelay(DEFAULT_DVRPTR3_TXDELAY), -m_dvmegaPort(DEFAULT_DVMEGA_PORT), -m_dvmegaVariant(DEFAULT_DVMEGA_VARIANT), -m_dvmegaRXInvert(DEFAULT_DVMEGA_RXINVERT), -m_dvmegaTXInvert(DEFAULT_DVMEGA_TXINVERT), -m_dvmegaTXDelay(DEFAULT_DVMEGA_TXDELAY), -m_dvmegaRXFrequency(DEFAULT_DVMEGA_RXFREQUENCY), -m_dvmegaTXFrequency(DEFAULT_DVMEGA_TXFREQUENCY), -m_dvmegaPower(DEFAULT_DVMEGA_POWER), -m_mmdvmPort(DEFAULT_MMDVM_PORT), -m_mmdvmRXInvert(DEFAULT_MMDVM_RXINVERT), -m_mmdvmTXInvert(DEFAULT_MMDVM_TXINVERT), -m_mmdvmPTTInvert(DEFAULT_MMDVM_PTTINVERT), -m_mmdvmTXDelay(DEFAULT_MMDVM_TXDELAY), -m_mmdvmRXLevel(DEFAULT_MMDVM_RXLEVEL), -m_mmdvmTXLevel(DEFAULT_MMDVM_TXLEVEL), -m_soundCardRXDevice(DEFAULT_SOUNDCARD_RXDEVICE), -m_soundCardTXDevice(DEFAULT_SOUNDCARD_TXDEVICE), -m_soundCardRXInvert(DEFAULT_SOUNDCARD_RXINVERT), -m_soundCardTXInvert(DEFAULT_SOUNDCARD_TXINVERT), -m_soundCardRXLevel(DEFAULT_SOUNDCARD_RXLEVEL), -m_soundCardTXLevel(DEFAULT_SOUNDCARD_TXLEVEL), -m_soundCardTXDelay(DEFAULT_SOUNDCARD_TXDELAY), -m_soundCardTXTail(DEFAULT_SOUNDCARD_TXTAIL), -m_splitLocalAddress(DEFAULT_SPLIT_LOCALADDRESS), -m_splitLocalPort(DEFAULT_SPLIT_LOCALPORT), -m_splitTXNames(), -m_splitRXNames(), -m_splitTimeout(DEFAULT_SPLIT_TIMEOUT), -m_icomPort(DEFAULT_ICOM_PORT) #if defined(MQTT) -,m_mqttHost(DEFAULT_MQTT_HOST), -m_mqttPort(DEFAULT_MQTT_PORT), -m_mqttAuth(DEFAULT_MQTT_AUTH), -m_mqttUsername(DEFAULT_MQTT_USERNAME), -m_mqttPassword(DEFAULT_MQTT_PASSWORD), -m_mqttKeepalive(DEFAULT_MQTT_KEEPALIVE), -m_mqttName(DEFAULT_MQTT_NAME) +// [MQTT] +static const std::string DEFAULT_MQTT_HOST = "127.0.0.1"; +static const unsigned int DEFAULT_MQTT_PORT = 1883U; +static const bool DEFAULT_MQTT_AUTH = false; +static const std::string DEFAULT_MQTT_USERNAME = std::string(); +static const std::string DEFAULT_MQTT_PASSWORD = std::string(); +static const unsigned int DEFAULT_MQTT_KEEPALIVE = 60U; +static const std::string DEFAULT_MQTT_NAME = "dstar-repeater"; #endif -{ - wxASSERT(config != NULL); - wxASSERT(!dir.IsEmpty()); - - wxString fileName = configName; - - if (!name.IsEmpty()) { - fileName = configName + wxT("_") + name; - m_name = wxT("/") + name + wxT("/"); - } - - m_fileName.Assign(dir, fileName); - - long temp; - double temp1; - - m_config->Read(m_name + KEY_CALLSIGN, &m_callsign, DEFAULT_CALLSIGN); - - m_config->Read(m_name + KEY_GATEWAY, &m_gateway, DEFAULT_GATEWAY); - - m_config->Read(m_name + KEY_MODE, &temp, long(DEFAULT_MODE)); - m_mode = DSTAR_MODE(temp); - - m_config->Read(m_name + KEY_ACK, &temp, long(DEFAULT_ACK)); - m_ack = ACK_TYPE(temp); - - m_config->Read(m_name + KEY_RESTRICTION, &m_restriction, DEFAULT_RESTRICTION); - - m_config->Read(m_name + KEY_RPT1_VALIDATION, &m_rpt1Validation, DEFAULT_RPT1_VALIDATION); - - m_config->Read(m_name + KEY_DTMF_BLANKING, &m_dtmfBlanking, DEFAULT_DTMF_BLANKING); - - m_config->Read(m_name + KEY_ERROR_REPLY, &m_errorReply, DEFAULT_ERROR_REPLY); - - m_config->Read(m_name + KEY_GATEWAY_ADDRESS, &m_gatewayAddress, DEFAULT_GATEWAY_ADDRESS); - - m_config->Read(m_name + KEY_GATEWAY_PORT, &temp, long(DEFAULT_GATEWAY_PORT)); - m_gatewayPort = (unsigned int)temp; - - m_config->Read(m_name + KEY_LOCAL_ADDRESS, &m_localAddress, DEFAULT_LOCAL_ADDRESS); - - m_config->Read(m_name + KEY_LOCAL_PORT, &temp, long(DEFAULT_LOCAL_PORT)); - m_localPort = (unsigned int)temp; - - m_config->Read(m_name + KEY_NETWORK_NAME, &m_networkName, DEFAULT_NETWORK_NAME); - - m_config->Read(m_name + KEY_MODEM_TYPE, &m_modemType, DEFAULT_MODEM_TYPE); - - m_config->Read(m_name + KEY_TIMEOUT, &temp, long(DEFAULT_TIMEOUT)); - m_timeout = (unsigned int)temp; - - m_config->Read(m_name + KEY_ACK_TIME, &temp, long(DEFAULT_ACK_TIME)); - m_ackTime = (unsigned int)temp; - - m_config->Read(m_name + KEY_BEACON_TIME, &temp, long(DEFAULT_BEACON_TIME)); - m_beaconTime = (unsigned int)temp; - - m_config->Read(m_name + KEY_BEACON_TEXT, &m_beaconText, DEFAULT_BEACON_TEXT); - - m_config->Read(m_name + KEY_BEACON_VOICE, &m_beaconVoice, DEFAULT_BEACON_VOICE); - - m_config->Read(m_name + KEY_LANGUAGE, &temp, long(DEFAULT_LANGUAGE)); - m_language = TEXT_LANG(temp); - - m_config->Read(m_name + KEY_ANNOUNCEMENT_ENABLED, &m_announcementEnabled, DEFAULT_ANNOUNCEMENT_ENABLED); - - m_config->Read(m_name + KEY_ANNOUNCEMENT_TIME, &temp, long(DEFAULT_ANNOUNCEMENT_TIME)); - m_announcementTime = (unsigned int)temp; - - m_config->Read(m_name + KEY_ANNOUNCEMENT_RECORD_RPT1, &m_announcementRecordRPT1, DEFAULT_ANNOUNCEMENT_RECORD_RPT1); - - m_config->Read(m_name + KEY_ANNOUNCEMENT_RECORD_RPT2, &m_announcementRecordRPT2, DEFAULT_ANNOUNCEMENT_RECORD_RPT2); - - m_config->Read(m_name + KEY_ANNOUNCEMENT_DELETE_RPT1, &m_announcementDeleteRPT1, DEFAULT_ANNOUNCEMENT_DELETE_RPT1); - - m_config->Read(m_name + KEY_ANNOUNCEMENT_DELETE_RPT2, &m_announcementDeleteRPT2, DEFAULT_ANNOUNCEMENT_DELETE_RPT2); - - m_config->Read(m_name + KEY_CONTROL_ENABLED, &m_controlEnabled, DEFAULT_CONTROL_ENABLED); - - m_config->Read(m_name + KEY_CONTROL_RPT1, &m_controlRpt1Callsign, DEFAULT_CONTROL_RPT1); - - m_config->Read(m_name + KEY_CONTROL_RPT2, &m_controlRpt2Callsign, DEFAULT_CONTROL_RPT2); - - m_config->Read(m_name + KEY_CONTROL_SHUTDOWN, &m_controlShutdown, DEFAULT_CONTROL_SHUTDOWN); - - m_config->Read(m_name + KEY_CONTROL_STARTUP, &m_controlStartup, DEFAULT_CONTROL_STARTUP); - - m_config->Read(m_name + KEY_CONTROL_STATUS1, &m_controlStatus1, DEFAULT_CONTROL_STATUS); - - m_config->Read(m_name + KEY_CONTROL_STATUS2, &m_controlStatus2, DEFAULT_CONTROL_STATUS); - - m_config->Read(m_name + KEY_CONTROL_STATUS3, &m_controlStatus3, DEFAULT_CONTROL_STATUS); - - m_config->Read(m_name + KEY_CONTROL_STATUS4, &m_controlStatus4, DEFAULT_CONTROL_STATUS); - - m_config->Read(m_name + KEY_CONTROL_STATUS5, &m_controlStatus5, DEFAULT_CONTROL_STATUS); - - m_config->Read(m_name + KEY_CONTROL_COMMAND1, &m_controlCommand1, DEFAULT_CONTROL_COMMAND); - - m_config->Read(m_name + KEY_CONTROL_COMMAND1_LINE, &m_controlCommand1Line, DEFAULT_CONTROL_COMMAND_LINE); - - m_config->Read(m_name + KEY_CONTROL_COMMAND2, &m_controlCommand2, DEFAULT_CONTROL_COMMAND); - - m_config->Read(m_name + KEY_CONTROL_COMMAND2_LINE, &m_controlCommand2Line, DEFAULT_CONTROL_COMMAND_LINE); - - m_config->Read(m_name + KEY_CONTROL_COMMAND3, &m_controlCommand3, DEFAULT_CONTROL_COMMAND); - - m_config->Read(m_name + KEY_CONTROL_COMMAND3_LINE, &m_controlCommand3Line, DEFAULT_CONTROL_COMMAND_LINE); - - m_config->Read(m_name + KEY_CONTROL_COMMAND4, &m_controlCommand4, DEFAULT_CONTROL_COMMAND); - - m_config->Read(m_name + KEY_CONTROL_COMMAND4_LINE, &m_controlCommand4Line, DEFAULT_CONTROL_COMMAND_LINE); - - m_config->Read(m_name + KEY_CONTROL_COMMAND5, &m_controlCommand5, DEFAULT_CONTROL_COMMAND); - - m_config->Read(m_name + KEY_CONTROL_COMMAND5_LINE, &m_controlCommand5Line, DEFAULT_CONTROL_COMMAND_LINE); - - m_config->Read(m_name + KEY_CONTROL_COMMAND6, &m_controlCommand6, DEFAULT_CONTROL_COMMAND); - - m_config->Read(m_name + KEY_CONTROL_COMMAND6_LINE, &m_controlCommand6Line, DEFAULT_CONTROL_COMMAND_LINE); - - m_config->Read(m_name + KEY_CONTROL_OUTPUT1, &m_controlOutput1, DEFAULT_CONTROL_OUTPUT); - - m_config->Read(m_name + KEY_CONTROL_OUTPUT2, &m_controlOutput2, DEFAULT_CONTROL_OUTPUT); - - m_config->Read(m_name + KEY_CONTROL_OUTPUT3, &m_controlOutput3, DEFAULT_CONTROL_OUTPUT); - - m_config->Read(m_name + KEY_CONTROL_OUTPUT4, &m_controlOutput4, DEFAULT_CONTROL_OUTPUT); - - m_config->Read(m_name + KEY_CONTROLLER_TYPE, &m_controllerType, DEFAULT_CONTROLLER_TYPE); - m_config->Read(m_name + KEY_SERIAL_CONFIG, &temp, long(DEFAULT_SERIAL_CONFIG)); - m_serialConfig = (unsigned int)temp; +// --------------------------------------------------------------------------- +// Constructor +// --------------------------------------------------------------------------- - m_config->Read(m_name + KEY_PTT_INVERT, &m_pttInvert, DEFAULT_PTT_INVERT); - - m_config->Read(m_name + KEY_ACTIVE_HANG_TIME, &temp, long(DEFAULT_ACTIVE_HANG_TIME)); - m_activeHangTime = (unsigned int)temp; - - m_config->Read(m_name + KEY_OUTPUT1, &m_output1, DEFAULT_OUTPUT); - - m_config->Read(m_name + KEY_OUTPUT2, &m_output2, DEFAULT_OUTPUT); - - m_config->Read(m_name + KEY_OUTPUT3, &m_output3, DEFAULT_OUTPUT); - - m_config->Read(m_name + KEY_OUTPUT4, &m_output4, DEFAULT_OUTPUT); - - m_config->Read(m_name + KEY_LOGGING, &m_logging, DEFAULT_LOGGING); - - m_config->Read(m_name + KEY_WINDOW_X, &temp, long(DEFAULT_WINDOW_X)); - m_x = int(temp); - - m_config->Read(m_name + KEY_WINDOW_Y, &temp, long(DEFAULT_WINDOW_Y)); - m_y = int(temp); - - m_config->Read(m_name + KEY_DVAP_PORT, &m_dvapPort, DEFAULT_DVAP_PORT); - - m_config->Read(m_name + KEY_DVAP_FREQUENCY, &temp, long(DEFAULT_DVAP_FREQUENCY)); - m_dvapFrequency = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVAP_POWER, &temp, long(DEFAULT_DVAP_POWER)); - m_dvapPower = int(temp); - - m_config->Read(m_name + KEY_DVAP_SQUELCH, &temp, long(DEFAULT_DVAP_SQUELCH)); - m_dvapSquelch = int(temp); - - m_config->Read(m_name + KEY_GMSK_INTERFACE, &temp, long(DEFAULT_GMSK_INTERFACE)); - m_gmskInterface = USB_INTERFACE(temp); - - m_config->Read(m_name + KEY_GMSK_ADDRESS, &temp, long(DEFAULT_GMSK_ADDRESS)); - m_gmskAddress = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVRPTR1_PORT, &m_dvrptr1Port, DEFAULT_DVRPTR1_PORT); - - m_config->Read(m_name + KEY_DVRPTR1_RXINVERT, &m_dvrptr1RXInvert, DEFAULT_DVRPTR1_RXINVERT); - - m_config->Read(m_name + KEY_DVRPTR1_TXINVERT, &m_dvrptr1TXInvert, DEFAULT_DVRPTR1_TXINVERT); - - m_config->Read(m_name + KEY_DVRPTR1_CHANNEL, &m_dvrptr1Channel, DEFAULT_DVRPTR1_CHANNEL); - - m_config->Read(m_name + KEY_DVRPTR1_MODLEVEL, &temp, long(DEFAULT_DVRPTR1_MODLEVEL)); - m_dvrptr1ModLevel = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVRPTR1_TXDELAY, &temp, long(DEFAULT_DVRPTR1_TXDELAY)); - m_dvrptr1TXDelay = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVRPTR2_CONNECTION, &temp, long(DEFAULT_DVRPTR2_CONNECTION)); - m_dvrptr2Connection = CONNECTION_TYPE(temp); - - m_config->Read(m_name + KEY_DVRPTR2_USBPORT, &m_dvrptr2USBPort, DEFAULT_DVRPTR2_USBPORT); - - m_config->Read(m_name + KEY_DVRPTR2_ADDRESS, &m_dvrptr2Address, DEFAULT_DVRPTR2_ADDRESS); - - m_config->Read(m_name + KEY_DVRPTR2_PORT, &temp, long(DEFAULT_DVRPTR2_PORT)); - m_dvrptr2Port = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVRPTR2_TXINVERT, &m_dvrptr2TXInvert, DEFAULT_DVRPTR2_TXINVERT); - - m_config->Read(m_name + KEY_DVRPTR2_MODLEVEL, &temp, long(DEFAULT_DVRPTR2_MODLEVEL)); - m_dvrptr2ModLevel = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVRPTR2_TXDELAY, &temp, long(DEFAULT_DVRPTR2_TXDELAY)); - m_dvrptr2TXDelay = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVRPTR3_CONNECTION, &temp, long(DEFAULT_DVRPTR3_CONNECTION)); - m_dvrptr3Connection = CONNECTION_TYPE(temp); - - m_config->Read(m_name + KEY_DVRPTR3_USBPORT, &m_dvrptr3USBPort, DEFAULT_DVRPTR3_USBPORT); - - m_config->Read(m_name + KEY_DVRPTR3_ADDRESS, &m_dvrptr3Address, DEFAULT_DVRPTR3_ADDRESS); - - m_config->Read(m_name + KEY_DVRPTR3_PORT, &temp, long(DEFAULT_DVRPTR3_PORT)); - m_dvrptr3Port = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVRPTR3_TXINVERT, &m_dvrptr3TXInvert, DEFAULT_DVRPTR3_TXINVERT); - - m_config->Read(m_name + KEY_DVRPTR3_MODLEVEL, &temp, long(DEFAULT_DVRPTR3_MODLEVEL)); - m_dvrptr3ModLevel = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVRPTR3_TXDELAY, &temp, long(DEFAULT_DVRPTR3_TXDELAY)); - m_dvrptr3TXDelay = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVMEGA_PORT, &m_dvmegaPort, DEFAULT_DVMEGA_PORT); - - m_config->Read(m_name + KEY_DVMEGA_VARIANT, &temp, long(DEFAULT_DVMEGA_VARIANT)); - m_dvmegaVariant = DVMEGA_VARIANT(temp); - - m_config->Read(m_name + KEY_DVMEGA_RXINVERT, &m_dvmegaRXInvert, DEFAULT_DVMEGA_RXINVERT); - - m_config->Read(m_name + KEY_DVMEGA_TXINVERT, &m_dvmegaTXInvert, DEFAULT_DVMEGA_TXINVERT); - - m_config->Read(m_name + KEY_DVMEGA_TXDELAY, &temp, long(DEFAULT_DVMEGA_TXDELAY)); - m_dvmegaTXDelay = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVMEGA_RXFREQUENCY, &temp, long(DEFAULT_DVMEGA_RXFREQUENCY)); - m_dvmegaRXFrequency = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVMEGA_TXFREQUENCY, &temp, long(DEFAULT_DVMEGA_TXFREQUENCY)); - m_dvmegaTXFrequency = (unsigned int)temp; - - m_config->Read(m_name + KEY_DVMEGA_POWER, &temp, long(DEFAULT_DVMEGA_POWER)); - m_dvmegaPower = (unsigned int)temp; - - m_config->Read(m_name + KEY_MMDVM_PORT, &m_mmdvmPort, DEFAULT_MMDVM_PORT); - - m_config->Read(m_name + KEY_MMDVM_RXINVERT, &m_mmdvmRXInvert, DEFAULT_MMDVM_RXINVERT); - - m_config->Read(m_name + KEY_MMDVM_TXINVERT, &m_mmdvmTXInvert, DEFAULT_MMDVM_TXINVERT); - - m_config->Read(m_name + KEY_MMDVM_PTTINVERT, &m_mmdvmPTTInvert, DEFAULT_MMDVM_PTTINVERT); - - m_config->Read(m_name + KEY_MMDVM_TXDELAY, &temp, long(DEFAULT_MMDVM_TXDELAY)); - m_mmdvmTXDelay = (unsigned int)temp; - - m_config->Read(m_name + KEY_MMDVM_RXLEVEL, &temp, long(DEFAULT_MMDVM_RXLEVEL)); - m_mmdvmRXLevel = (unsigned int)temp; - - m_config->Read(m_name + KEY_MMDVM_TXLEVEL, &temp, long(DEFAULT_MMDVM_TXLEVEL)); - m_mmdvmTXLevel = (unsigned int)temp; - - m_config->Read(m_name + KEY_SOUNDCARD_RXDEVICE, &m_soundCardRXDevice, DEFAULT_SOUNDCARD_RXDEVICE); - - m_config->Read(m_name + KEY_SOUNDCARD_TXDEVICE, &m_soundCardTXDevice, DEFAULT_SOUNDCARD_TXDEVICE); - - m_config->Read(m_name + KEY_SOUNDCARD_RXINVERT, &m_soundCardRXInvert, DEFAULT_SOUNDCARD_RXINVERT); - - m_config->Read(m_name + KEY_SOUNDCARD_TXINVERT, &m_soundCardTXInvert, DEFAULT_SOUNDCARD_TXINVERT); - - m_config->Read(m_name + KEY_SOUNDCARD_RXLEVEL, &temp1, double(DEFAULT_SOUNDCARD_RXLEVEL)); - m_soundCardRXLevel = wxFloat32(temp1); - - m_config->Read(m_name + KEY_SOUNDCARD_TXLEVEL, &temp1, double(DEFAULT_SOUNDCARD_TXLEVEL)); - m_soundCardTXLevel = wxFloat32(temp1); - - m_config->Read(m_name + KEY_SOUNDCARD_TXDELAY, &temp, long(DEFAULT_SOUNDCARD_TXDELAY)); - m_soundCardTXDelay = (unsigned int)temp; - - m_config->Read(m_name + KEY_SOUNDCARD_TXTAIL, &temp, long(DEFAULT_SOUNDCARD_TXTAIL)); - m_soundCardTXTail = (unsigned int)temp; - - m_config->Read(m_name + KEY_SPLIT_LOCALADDRESS, &m_splitLocalAddress, DEFAULT_SPLIT_LOCALADDRESS); - - m_config->Read(m_name + KEY_SPLIT_LOCALPORT, &temp, long(DEFAULT_SPLIT_LOCALPORT)); - m_splitLocalPort = (unsigned int)temp; - - for (unsigned int i = 0U; i < SPLIT_TX_COUNT; i++) { - wxString name; - name.Printf(wxT("%s%u"), KEY_SPLIT_TXNAME.c_str(), i); - - wxString value; - m_config->Read(m_name + name, &value, DEFAULT_SPLIT_TXNAME); - m_splitTXNames.Add(value); - } - - for (unsigned int i = 0U; i < SPLIT_RX_COUNT; i++) { - wxString name; - name.Printf(wxT("%s%u"), KEY_SPLIT_RXNAME.c_str(), i); - - wxString value; - m_config->Read(m_name + name, &value, DEFAULT_SPLIT_RXNAME); - m_splitRXNames.Add(value); - } - - m_config->Read(m_name + KEY_SPLIT_TIMEOUT, &temp, long(DEFAULT_SPLIT_TIMEOUT)); - m_splitTimeout = (unsigned int)temp; - - m_config->Read(m_name + KEY_ICOM_PORT, &m_icomPort, DEFAULT_ICOM_PORT); -} - -CDStarRepeaterConfig::~CDStarRepeaterConfig() -{ - delete m_config; -} - -#else - -CDStarRepeaterConfig::CDStarRepeaterConfig(const wxString& dir, const wxString& configName, const wxString& name, const bool mustExist) : -m_fileName(), +CDStarRepeaterConfig::CDStarRepeaterConfig(const std::string& filePath) : m_callsign(DEFAULT_CALLSIGN), m_gateway(DEFAULT_GATEWAY), m_mode(DEFAULT_MODE), @@ -742,6 +243,12 @@ m_restriction(DEFAULT_RESTRICTION), m_rpt1Validation(DEFAULT_RPT1_VALIDATION), m_dtmfBlanking(DEFAULT_DTMF_BLANKING), m_errorReply(DEFAULT_ERROR_REPLY), +m_logFilePath(DEFAULT_LOG_FILE_PATH), +m_logFileLevel(DEFAULT_LOG_FILE_LEVEL), +m_logDisplayLevel(DEFAULT_LOG_DISPLAY_LEVEL), +m_logMQTTLevel(DEFAULT_LOG_MQTT_LEVEL), +m_dataDir(DEFAULT_DATA_DIR), +m_audioDir(DEFAULT_AUDIO_DIR), m_gatewayAddress(DEFAULT_GATEWAY_ADDRESS), m_gatewayPort(DEFAULT_GATEWAY_PORT), m_localAddress(DEFAULT_LOCAL_ADDRESS), @@ -795,8 +302,9 @@ m_output2(DEFAULT_OUTPUT), m_output3(DEFAULT_OUTPUT), m_output4(DEFAULT_OUTPUT), m_logging(DEFAULT_LOGGING), -m_x(DEFAULT_WINDOW_X), -m_y(DEFAULT_WINDOW_Y), +m_whitelistFile(), +m_blacklistFile(), +m_greylistFile(), m_dvapPort(DEFAULT_DVAP_PORT), m_dvapFrequency(DEFAULT_DVAP_FREQUENCY), m_dvapPower(DEFAULT_DVAP_POWER), @@ -862,397 +370,461 @@ m_icomPort(DEFAULT_ICOM_PORT) ,m_mqttName(DEFAULT_MQTT_NAME) #endif { - wxASSERT(!dir.IsEmpty()); - - wxString fileName = configName; - if (!name.IsEmpty()) - fileName = configName + wxT("_") + name; - - m_fileName.Assign(dir, fileName); - - wxTextFile file(m_fileName.GetFullPath()); - - if (!file.Exists()) { - if (mustExist) - throw std::runtime_error("Configuration file does not exist"); - else - return; - } - - if (!file.Open()) - throw std::runtime_error("Cannot open the configuration file"); - - wxString* splitTXName = new wxString[SPLIT_TX_COUNT]; - wxString* splitRXName = new wxString[SPLIT_RX_COUNT]; - - long temp1; - unsigned long temp2; - double temp3; + std::ifstream file(filePath); + if (!file.is_open()) + throw std::runtime_error("Cannot open configuration file: " + filePath); + + // Pre-size Split arrays at the maximum count; empty slots are ignored later. + std::string splitTXName[SPLIT_TX_COUNT]; + std::string splitRXName[SPLIT_RX_COUNT]; + + std::string section; + std::string line; + while (std::getline(file, line)) { + // Strip trailing carriage-return so Windows-style CRLF files parse cleanly. + if (!line.empty() && line.back() == '\r') + line.pop_back(); + + if (line.empty() || line[0] == '#') + continue; - for (wxString str = file.GetFirstLine(); - !file.Eof(); - str = file.GetNextLine()) { - if (str.IsEmpty() || str.GetChar(0U) == wxT('#')) + // Section header: "[Section Name]" + if (line[0] == '[') { + std::string::size_type close = line.find(']'); + if (close != std::string::npos) + section = line.substr(1, close - 1); continue; + } - int n = str.Find(wxT('=')); - if (n == wxNOT_FOUND) + // Key=Value pair + std::string::size_type eq = line.find('='); + if (eq == std::string::npos) continue; - wxString key = str.Left(n); - wxString val = str.Mid(n + 1U); - - if (key.IsSameAs(KEY_CALLSIGN)) { - m_callsign = val; - } else if (key.IsSameAs(KEY_GATEWAY)) { - m_gateway = val; - } else if (key.IsSameAs(KEY_MODE)) { - val.ToLong(&temp1); - m_mode = DSTAR_MODE(temp1); - } else if (key.IsSameAs(KEY_ACK)) { - val.ToLong(&temp1); - m_ack = ACK_TYPE(temp1); - } else if (key.IsSameAs(KEY_RESTRICTION)) { - val.ToLong(&temp1); - m_restriction = temp1 == 1L; - } else if (key.IsSameAs(KEY_RPT1_VALIDATION)) { - val.ToLong(&temp1); - m_rpt1Validation = temp1 == 1L; - } else if (key.IsSameAs(KEY_DTMF_BLANKING)) { - val.ToLong(&temp1); - m_dtmfBlanking = temp1 == 1L; - } else if (key.IsSameAs(KEY_ERROR_REPLY)) { - val.ToLong(&temp1); - m_errorReply = temp1 == 1L; - } else if (key.IsSameAs(KEY_GATEWAY_ADDRESS)) { - m_gatewayAddress = val; - } else if (key.IsSameAs(KEY_GATEWAY_PORT)) { - val.ToULong(&temp2); - m_gatewayPort = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_LOCAL_ADDRESS)) { - m_localAddress = val; - } else if (key.IsSameAs(KEY_LOCAL_PORT)) { - val.ToULong(&temp2); - m_localPort = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_NETWORK_NAME)) { - m_networkName = val; - } else if (key.IsSameAs(KEY_MODEM_TYPE)) { - m_modemType = val; - } else if (key.IsSameAs(KEY_TIMEOUT)) { - val.ToULong(&temp2); - m_timeout = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_ACK_TIME)) { - val.ToULong(&temp2); - m_ackTime = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_BEACON_TIME)) { - val.ToULong(&temp2); - m_beaconTime = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_BEACON_TEXT)) { - m_beaconText = val; - } else if (key.IsSameAs(KEY_BEACON_VOICE)) { - val.ToLong(&temp1); - m_beaconVoice = temp1 == 1L; - } else if (key.IsSameAs(KEY_LANGUAGE)) { - val.ToLong(&temp1); - m_language = TEXT_LANG(temp1); - } else if (key.IsSameAs(KEY_ANNOUNCEMENT_ENABLED)) { - val.ToLong(&temp1); - m_announcementEnabled = temp1 == 1L; - } else if (key.IsSameAs(KEY_ANNOUNCEMENT_TIME)) { - val.ToULong(&temp2); - m_announcementTime = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_ANNOUNCEMENT_RECORD_RPT1)) { - m_announcementRecordRPT1 = val; - } else if (key.IsSameAs(KEY_ANNOUNCEMENT_RECORD_RPT2)) { - m_announcementRecordRPT2 = val; - } else if (key.IsSameAs(KEY_ANNOUNCEMENT_DELETE_RPT1)) { - m_announcementDeleteRPT1 = val; - } else if (key.IsSameAs(KEY_ANNOUNCEMENT_DELETE_RPT2)) { - m_announcementDeleteRPT2 = val; - } else if (key.IsSameAs(KEY_CONTROL_ENABLED)) { - val.ToLong(&temp1); - m_controlEnabled = temp1 == 1L; - } else if (key.IsSameAs(KEY_CONTROL_RPT1)) { - m_controlRpt1Callsign = val; - } else if (key.IsSameAs(KEY_CONTROL_RPT2)) { - m_controlRpt2Callsign = val; - } else if (key.IsSameAs(KEY_CONTROL_SHUTDOWN)) { - m_controlShutdown = val; - } else if (key.IsSameAs(KEY_CONTROL_STARTUP)) { - m_controlStartup = val; - } else if (key.IsSameAs(KEY_CONTROL_STATUS1)) { - m_controlStatus1 = val; - } else if (key.IsSameAs(KEY_CONTROL_STATUS2)) { - m_controlStatus2 = val; - } else if (key.IsSameAs(KEY_CONTROL_STATUS3)) { - m_controlStatus3 = val; - } else if (key.IsSameAs(KEY_CONTROL_STATUS4)) { - m_controlStatus4 = val; - } else if (key.IsSameAs(KEY_CONTROL_STATUS5)) { - m_controlStatus5 = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND1)) { - m_controlCommand1 = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND1_LINE)) { - m_controlCommand1Line = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND2)) { - m_controlCommand2 = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND2_LINE)) { - m_controlCommand2Line = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND3)) { - m_controlCommand3 = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND3_LINE)) { - m_controlCommand3Line = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND4)) { - m_controlCommand4 = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND4_LINE)) { - m_controlCommand4Line = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND5)) { - m_controlCommand5 = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND5_LINE)) { - m_controlCommand5Line = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND6)) { - m_controlCommand6 = val; - } else if (key.IsSameAs(KEY_CONTROL_COMMAND6_LINE)) { - m_controlCommand6Line = val; - } else if (key.IsSameAs(KEY_CONTROL_OUTPUT1)) { - m_controlOutput1 = val; - } else if (key.IsSameAs(KEY_CONTROL_OUTPUT2)) { - m_controlOutput2 = val; - } else if (key.IsSameAs(KEY_CONTROL_OUTPUT3)) { - m_controlOutput3 = val; - } else if (key.IsSameAs(KEY_CONTROL_OUTPUT4)) { - m_controlOutput4 = val; - } else if (key.IsSameAs(KEY_CONTROLLER_TYPE)) { - m_controllerType = val; - } else if (key.IsSameAs(KEY_SERIAL_CONFIG)) { - val.ToULong(&temp2); - m_serialConfig = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_PTT_INVERT)) { - val.ToLong(&temp1); - m_pttInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_ACTIVE_HANG_TIME)) { - val.ToULong(&temp2); - m_activeHangTime = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_OUTPUT1)) { - val.ToLong(&temp1); - m_output1 = temp1 == 1L; - } else if (key.IsSameAs(KEY_OUTPUT2)) { - val.ToLong(&temp1); - m_output2 = temp1 == 1L; - } else if (key.IsSameAs(KEY_OUTPUT3)) { - val.ToLong(&temp1); - m_output3 = temp1 == 1L; - } else if (key.IsSameAs(KEY_OUTPUT4)) { - val.ToLong(&temp1); - m_output4 = temp1 == 1L; - } else if (key.IsSameAs(KEY_LOGGING)) { - val.ToLong(&temp1); - m_logging = temp1 == 1L; - } else if (key.IsSameAs(KEY_WINDOW_X)) { - val.ToLong(&temp1); - m_x = int(temp1); - } else if (key.IsSameAs(KEY_WINDOW_Y)) { - val.ToLong(&temp1); - m_y = int(temp1); - } else if (key.IsSameAs(KEY_DVAP_PORT)) { - m_dvapPort = val; - } else if (key.IsSameAs(KEY_DVAP_FREQUENCY)) { - val.ToULong(&temp2); - m_dvapFrequency = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVAP_POWER)) { - val.ToLong(&temp1); - m_dvapPower = int(temp1); - } else if (key.IsSameAs(KEY_DVAP_SQUELCH)) { - val.ToLong(&temp1); - m_dvapSquelch = int(temp1); - } else if (key.IsSameAs(KEY_GMSK_ADDRESS)) { - val.ToULong(&temp2); - m_gmskAddress = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVRPTR1_PORT)) { - m_dvrptr1Port = val; - } else if (key.IsSameAs(KEY_DVRPTR1_RXINVERT)) { - val.ToLong(&temp1); - m_dvrptr1RXInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_DVRPTR1_TXINVERT)) { - val.ToLong(&temp1); - m_dvrptr1TXInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_DVRPTR1_CHANNEL)) { - val.ToLong(&temp1); - m_dvrptr1Channel = temp1 == 1L; - } else if (key.IsSameAs(KEY_DVRPTR1_MODLEVEL)) { - val.ToULong(&temp2); - m_dvrptr1ModLevel = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVRPTR1_TXDELAY)) { - val.ToULong(&temp2); - m_dvrptr1TXDelay = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVRPTR2_CONNECTION)) { - val.ToLong(&temp1); - m_dvrptr2Connection = CONNECTION_TYPE(temp1); - } else if (key.IsSameAs(KEY_DVRPTR2_USBPORT)) { - m_dvrptr2USBPort = val; - } else if (key.IsSameAs(KEY_DVRPTR2_ADDRESS)) { - m_dvrptr2Address = val; - } else if (key.IsSameAs(KEY_DVRPTR2_PORT)) { - val.ToULong(&temp2); - m_dvrptr2Port = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVRPTR2_TXINVERT)) { - val.ToLong(&temp1); - m_dvrptr2TXInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_DVRPTR2_MODLEVEL)) { - val.ToULong(&temp2); - m_dvrptr2ModLevel = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVRPTR2_TXDELAY)) { - val.ToULong(&temp2); - m_dvrptr2TXDelay = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVRPTR3_CONNECTION)) { - val.ToLong(&temp1); - m_dvrptr3Connection = CONNECTION_TYPE(temp1); - } else if (key.IsSameAs(KEY_DVRPTR3_USBPORT)) { - m_dvrptr3USBPort = val; - } else if (key.IsSameAs(KEY_DVRPTR3_ADDRESS)) { - m_dvrptr3Address = val; - } else if (key.IsSameAs(KEY_DVRPTR3_PORT)) { - val.ToULong(&temp2); - m_dvrptr3Port = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVRPTR3_TXINVERT)) { - val.ToLong(&temp1); - m_dvrptr3TXInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_DVRPTR3_MODLEVEL)) { - val.ToULong(&temp2); - m_dvrptr3ModLevel = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVRPTR3_TXDELAY)) { - val.ToULong(&temp2); - m_dvrptr3TXDelay = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVMEGA_PORT)) { - m_dvmegaPort = val; - } else if (key.IsSameAs(KEY_DVMEGA_VARIANT)) { - val.ToLong(&temp1); - m_dvmegaVariant = DVMEGA_VARIANT(temp1); - } else if (key.IsSameAs(KEY_DVMEGA_RXINVERT)) { - val.ToLong(&temp1); - m_dvmegaRXInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_DVMEGA_TXINVERT)) { - val.ToLong(&temp1); - m_dvmegaTXInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_DVMEGA_TXDELAY)) { - val.ToULong(&temp2); - m_dvmegaTXDelay = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVMEGA_RXFREQUENCY)) { - val.ToULong(&temp2); - m_dvmegaRXFrequency = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVMEGA_TXFREQUENCY)) { - val.ToULong(&temp2); - m_dvmegaTXFrequency = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_DVMEGA_POWER)) { - val.ToULong(&temp2); - m_dvmegaPower = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_MMDVM_PORT)) { - m_mmdvmPort = val; - } else if (key.IsSameAs(KEY_MMDVM_RXINVERT)) { - val.ToLong(&temp1); - m_mmdvmRXInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_MMDVM_TXINVERT)) { - val.ToLong(&temp1); - m_mmdvmTXInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_MMDVM_PTTINVERT)) { - val.ToLong(&temp1); - m_mmdvmPTTInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_MMDVM_TXDELAY)) { - val.ToULong(&temp2); - m_mmdvmTXDelay = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_MMDVM_RXLEVEL)) { - val.ToULong(&temp2); - m_mmdvmRXLevel = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_MMDVM_TXLEVEL)) { - val.ToULong(&temp2); - m_mmdvmTXLevel = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_SOUNDCARD_RXDEVICE)) { - m_soundCardRXDevice = val; - } else if (key.IsSameAs(KEY_SOUNDCARD_TXDEVICE)) { - m_soundCardTXDevice = val; - } else if (key.IsSameAs(KEY_SOUNDCARD_RXINVERT)) { - val.ToLong(&temp1); - m_soundCardRXInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_SOUNDCARD_TXINVERT)) { - val.ToLong(&temp1); - m_soundCardTXInvert = temp1 == 1L; - } else if (key.IsSameAs(KEY_SOUNDCARD_RXLEVEL)) { - val.ToDouble(&temp3); - m_soundCardRXLevel = wxFloat32(temp3); - } else if (key.IsSameAs(KEY_SOUNDCARD_TXLEVEL)) { - val.ToDouble(&temp3); - m_soundCardTXLevel = wxFloat32(temp3); - } else if (key.IsSameAs(KEY_SOUNDCARD_TXDELAY)) { - val.ToULong(&temp2); - m_soundCardTXDelay = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_SOUNDCARD_TXTAIL)) { - val.ToULong(&temp2); - m_soundCardTXTail = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_ICOM_PORT)) { - m_icomPort = val; -#if defined(MQTT) - } else if (key.IsSameAs(KEY_MQTT_HOST)) { - m_mqttHost = val; - } else if (key.IsSameAs(KEY_MQTT_PORT)) { - val.ToULong(&temp2); - m_mqttPort = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_MQTT_AUTH)) { - val.ToLong(&temp1); - m_mqttAuth = temp1 == 1L; - } else if (key.IsSameAs(KEY_MQTT_USERNAME)) { - m_mqttUsername = val; - } else if (key.IsSameAs(KEY_MQTT_PASSWORD)) { - m_mqttPassword = val; - } else if (key.IsSameAs(KEY_MQTT_KEEPALIVE)) { - val.ToULong(&temp2); - m_mqttKeepalive = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_MQTT_NAME)) { - m_mqttName = val; -#endif - } else if (key.IsSameAs(KEY_SPLIT_LOCALADDRESS)) { - m_splitLocalAddress = val; - } else if (key.IsSameAs(KEY_SPLIT_LOCALPORT)) { - val.ToULong(&temp2); - m_splitLocalPort = (unsigned int)temp2; - } else if (key.IsSameAs(KEY_SPLIT_TIMEOUT)) { - val.ToULong(&temp2); - m_splitTimeout = (unsigned int)temp2; - } else { - for (unsigned int i = 0U; i < SPLIT_TX_COUNT; i++) { - wxString name; - name.Printf(wxT("%s%u"), KEY_SPLIT_TXNAME.c_str(), i); - if (key.IsSameAs(name)) - splitTXName[i] = val; + const std::string key = line.substr(0, eq); + const std::string val = line.substr(eq + 1U); + + // --------------------------------------------------------------------------- + // [General] + // --------------------------------------------------------------------------- + if (section == SECTION_GENERAL) { + if (key == "Callsign") + m_callsign = val; + else if (key == "Gateway") + m_gateway = val; + else if (key == "Mode") + m_mode = DSTAR_MODE(std::stol(val)); + else if (key == "Ack") + m_ack = ACK_TYPE(std::stol(val)); + else if (key == "Restriction") + m_restriction = std::stol(val) == 1L; + else if (key == "RPT1Validation") + m_rpt1Validation = std::stol(val) == 1L; + else if (key == "DTMFBlanking") + m_dtmfBlanking = std::stol(val) == 1L; + else if (key == "ErrorReply") + m_errorReply = std::stol(val) == 1L; + + // --------------------------------------------------------------------------- + // [Log] + // --------------------------------------------------------------------------- + } else if (section == SECTION_LOG) { + if (key == "FilePath") + m_logFilePath = val; + else if (key == "FileLevel") + m_logFileLevel = (unsigned int)std::stoul(val); + else if (key == "DisplayLevel") + m_logDisplayLevel = (unsigned int)std::stoul(val); + else if (key == "MQTTLevel") + m_logMQTTLevel = (unsigned int)std::stoul(val); + + // --------------------------------------------------------------------------- + // [Paths] + // --------------------------------------------------------------------------- + } else if (section == SECTION_PATHS) { + if (key == "Data") + m_dataDir = val; + else if (key == "Audio") + m_audioDir = val; + + // --------------------------------------------------------------------------- + // [Network] + // --------------------------------------------------------------------------- + } else if (section == SECTION_NETWORK) { + if (key == "GatewayAddress") + m_gatewayAddress = val; + else if (key == "GatewayPort") + m_gatewayPort = (unsigned int)std::stoul(val); + else if (key == "LocalAddress") + m_localAddress = val; + else if (key == "LocalPort") + m_localPort = (unsigned int)std::stoul(val); + else if (key == "Name") + m_networkName = val; + + // --------------------------------------------------------------------------- + // [Modem] + // --------------------------------------------------------------------------- + } else if (section == SECTION_MODEM) { + if (key == "Type") + m_modemType = val; + + // --------------------------------------------------------------------------- + // [Times] + // --------------------------------------------------------------------------- + } else if (section == SECTION_TIMES) { + if (key == "Timeout") + m_timeout = (unsigned int)std::stoul(val); + else if (key == "AckTime") + m_ackTime = (unsigned int)std::stoul(val); + + // --------------------------------------------------------------------------- + // [Beacon] + // --------------------------------------------------------------------------- + } else if (section == SECTION_BEACON) { + if (key == "Time") + m_beaconTime = (unsigned int)std::stoul(val); + else if (key == "Text") + m_beaconText = val; + else if (key == "Voice") + m_beaconVoice = std::stol(val) == 1L; + else if (key == "Language") + m_language = TEXT_LANG(std::stol(val)); + + // --------------------------------------------------------------------------- + // [Announcement] + // --------------------------------------------------------------------------- + } else if (section == SECTION_ANNOUNCEMENT) { + if (key == "Enabled") + m_announcementEnabled = std::stol(val) == 1L; + else if (key == "Time") + m_announcementTime = (unsigned int)std::stoul(val); + else if (key == "RecordRPT1") + m_announcementRecordRPT1 = val; + else if (key == "RecordRPT2") + m_announcementRecordRPT2 = val; + else if (key == "DeleteRPT1") + m_announcementDeleteRPT1 = val; + else if (key == "DeleteRPT2") + m_announcementDeleteRPT2 = val; + + // --------------------------------------------------------------------------- + // [Control] + // --------------------------------------------------------------------------- + } else if (section == SECTION_CONTROL) { + if (key == "Enabled") + m_controlEnabled = std::stol(val) == 1L; + else if (key == "RPT1") + m_controlRpt1Callsign = val; + else if (key == "RPT2") + m_controlRpt2Callsign = val; + else if (key == "Shutdown") + m_controlShutdown = val; + else if (key == "Startup") + m_controlStartup = val; + else if (key == "Status1") + m_controlStatus1 = val; + else if (key == "Status2") + m_controlStatus2 = val; + else if (key == "Status3") + m_controlStatus3 = val; + else if (key == "Status4") + m_controlStatus4 = val; + else if (key == "Status5") + m_controlStatus5 = val; + else if (key == "Command1") + m_controlCommand1 = val; + else if (key == "Command1Line") + m_controlCommand1Line = val; + else if (key == "Command2") + m_controlCommand2 = val; + else if (key == "Command2Line") + m_controlCommand2Line = val; + else if (key == "Command3") + m_controlCommand3 = val; + else if (key == "Command3Line") + m_controlCommand3Line = val; + else if (key == "Command4") + m_controlCommand4 = val; + else if (key == "Command4Line") + m_controlCommand4Line = val; + else if (key == "Command5") + m_controlCommand5 = val; + else if (key == "Command5Line") + m_controlCommand5Line = val; + else if (key == "Command6") + m_controlCommand6 = val; + else if (key == "Command6Line") + m_controlCommand6Line = val; + else if (key == "Output1") + m_controlOutput1 = val; + else if (key == "Output2") + m_controlOutput2 = val; + else if (key == "Output3") + m_controlOutput3 = val; + else if (key == "Output4") + m_controlOutput4 = val; + + // --------------------------------------------------------------------------- + // [Controller] + // --------------------------------------------------------------------------- + } else if (section == SECTION_CONTROLLER) { + if (key == "Type") + m_controllerType = val; + else if (key == "SerialConfig") + m_serialConfig = (unsigned int)std::stoul(val); + else if (key == "PTTInvert") + m_pttInvert = std::stol(val) == 1L; + else if (key == "ActiveHangTime") + m_activeHangTime = (unsigned int)std::stoul(val); + + // --------------------------------------------------------------------------- + // [Outputs] + // --------------------------------------------------------------------------- + } else if (section == SECTION_OUTPUTS) { + if (key == "Output1") + m_output1 = std::stol(val) == 1L; + else if (key == "Output2") + m_output2 = std::stol(val) == 1L; + else if (key == "Output3") + m_output3 = std::stol(val) == 1L; + else if (key == "Output4") + m_output4 = std::stol(val) == 1L; + + // --------------------------------------------------------------------------- + // [Frame Logging] + // --------------------------------------------------------------------------- + } else if (section == SECTION_FRAMELOGGING) { + if (key == "Enabled") + m_logging = std::stol(val) == 1L; + + // --------------------------------------------------------------------------- + // [Whitelist] / [Blacklist] / [Greylist] + // --------------------------------------------------------------------------- + } else if (section == SECTION_WHITELIST) { + if (key == "File") + m_whitelistFile = val; + } else if (section == SECTION_BLACKLIST) { + if (key == "File") + m_blacklistFile = val; + } else if (section == SECTION_GREYLIST) { + if (key == "File") + m_greylistFile = val; + + // --------------------------------------------------------------------------- + // [DVAP] + // --------------------------------------------------------------------------- + } else if (section == SECTION_DVAP) { + if (key == "Port") + m_dvapPort = val; + else if (key == "Frequency") + m_dvapFrequency = (unsigned int)std::stoul(val); + else if (key == "Power") + m_dvapPower = int(std::stol(val)); + else if (key == "Squelch") + m_dvapSquelch = int(std::stol(val)); + + // --------------------------------------------------------------------------- + // [GMSK] + // --------------------------------------------------------------------------- + } else if (section == SECTION_GMSK) { + if (key == "InterfaceType") + m_gmskInterface = (USB_INTERFACE)std::stoul(val); + else if (key == "Address") + m_gmskAddress = (unsigned int)std::stoul(val); + + // --------------------------------------------------------------------------- + // [DV-RPTR V1] + // --------------------------------------------------------------------------- + } else if (section == SECTION_DVRPTR1) { + if (key == "Port") + m_dvrptr1Port = val; + else if (key == "RXInvert") + m_dvrptr1RXInvert = std::stol(val) == 1L; + else if (key == "TXInvert") + m_dvrptr1TXInvert = std::stol(val) == 1L; + else if (key == "Channel") + m_dvrptr1Channel = std::stol(val) == 1L; + else if (key == "ModLevel") + m_dvrptr1ModLevel = (unsigned int)std::stoul(val); + else if (key == "TXDelay") + m_dvrptr1TXDelay = (unsigned int)std::stoul(val); + + // --------------------------------------------------------------------------- + // [DV-RPTR V2] + // --------------------------------------------------------------------------- + } else if (section == SECTION_DVRPTR2) { + if (key == "Connection") + m_dvrptr2Connection = CONNECTION_TYPE(std::stol(val)); + else if (key == "USBPort") + m_dvrptr2USBPort = val; + else if (key == "Address") + m_dvrptr2Address = val; + else if (key == "Port") + m_dvrptr2Port = (unsigned int)std::stoul(val); + else if (key == "TXInvert") + m_dvrptr2TXInvert = std::stol(val) == 1L; + else if (key == "ModLevel") + m_dvrptr2ModLevel = (unsigned int)std::stoul(val); + else if (key == "TXDelay") + m_dvrptr2TXDelay = (unsigned int)std::stoul(val); + + // --------------------------------------------------------------------------- + // [DV-RPTR V3] + // --------------------------------------------------------------------------- + } else if (section == SECTION_DVRPTR3) { + if (key == "Connection") + m_dvrptr3Connection = CONNECTION_TYPE(std::stol(val)); + else if (key == "USBPort") + m_dvrptr3USBPort = val; + else if (key == "Address") + m_dvrptr3Address = val; + else if (key == "Port") + m_dvrptr3Port = (unsigned int)std::stoul(val); + else if (key == "TXInvert") + m_dvrptr3TXInvert = std::stol(val) == 1L; + else if (key == "ModLevel") + m_dvrptr3ModLevel = (unsigned int)std::stoul(val); + else if (key == "TXDelay") + m_dvrptr3TXDelay = (unsigned int)std::stoul(val); + + // --------------------------------------------------------------------------- + // [DVMEGA] + // --------------------------------------------------------------------------- + } else if (section == SECTION_DVMEGA) { + if (key == "Port") + m_dvmegaPort = val; + else if (key == "Variant") + m_dvmegaVariant = DVMEGA_VARIANT(std::stol(val)); + else if (key == "RXInvert") + m_dvmegaRXInvert = std::stol(val) == 1L; + else if (key == "TXInvert") + m_dvmegaTXInvert = std::stol(val) == 1L; + else if (key == "TXDelay") + m_dvmegaTXDelay = (unsigned int)std::stoul(val); + else if (key == "RXFrequency") + m_dvmegaRXFrequency = (unsigned int)std::stoul(val); + else if (key == "TXFrequency") + m_dvmegaTXFrequency = (unsigned int)std::stoul(val); + else if (key == "Power") + m_dvmegaPower = (unsigned int)std::stoul(val); + + // --------------------------------------------------------------------------- + // [MMDVM] + // --------------------------------------------------------------------------- + } else if (section == SECTION_MMDVM) { + if (key == "Port") + m_mmdvmPort = val; + else if (key == "RXInvert") + m_mmdvmRXInvert = std::stol(val) == 1L; + else if (key == "TXInvert") + m_mmdvmTXInvert = std::stol(val) == 1L; + else if (key == "PTTInvert") + m_mmdvmPTTInvert = std::stol(val) == 1L; + else if (key == "TXDelay") + m_mmdvmTXDelay = (unsigned int)std::stoul(val); + else if (key == "RXLevel") + m_mmdvmRXLevel = (unsigned int)std::stoul(val); + else if (key == "TXLevel") + m_mmdvmTXLevel = (unsigned int)std::stoul(val); + + // --------------------------------------------------------------------------- + // [Sound Card] + // --------------------------------------------------------------------------- + } else if (section == SECTION_SOUNDCARD) { + if (key == "RXDevice") + m_soundCardRXDevice = val; + else if (key == "TXDevice") + m_soundCardTXDevice = val; + else if (key == "RXInvert") + m_soundCardRXInvert = std::stol(val) == 1L; + else if (key == "TXInvert") + m_soundCardTXInvert = std::stol(val) == 1L; + else if (key == "RXLevel") + m_soundCardRXLevel = float(std::stod(val)); + else if (key == "TXLevel") + m_soundCardTXLevel = float(std::stod(val)); + else if (key == "TXDelay") + m_soundCardTXDelay = (unsigned int)std::stoul(val); + else if (key == "TXTail") + m_soundCardTXTail = (unsigned int)std::stoul(val); + + // --------------------------------------------------------------------------- + // [Split] + // --------------------------------------------------------------------------- + } else if (section == SECTION_SPLIT) { + if (key == "LocalAddress") + m_splitLocalAddress = val; + else if (key == "LocalPort") + m_splitLocalPort = (unsigned int)std::stoul(val); + else if (key == "Timeout") + m_splitTimeout = (unsigned int)std::stoul(val); + else { + // TXName0..TXNameN and RXName0..RXNameN + for (unsigned int i = 0U; i < SPLIT_TX_COUNT; i++) { + char namebuf[32]; + ::snprintf(namebuf, sizeof(namebuf), "TXName%u", i); + if (key == namebuf) { + splitTXName[i] = val; + break; + } + } + for (unsigned int i = 0U; i < SPLIT_RX_COUNT; i++) { + char namebuf[32]; + ::snprintf(namebuf, sizeof(namebuf), "RXName%u", i); + if (key == namebuf) { + splitRXName[i] = val; + break; + } + } } - for (unsigned int i = 0U; i < SPLIT_RX_COUNT; i++) { - wxString name; - name.Printf(wxT("%s%u"), KEY_SPLIT_RXNAME.c_str(), i); - if (key.IsSameAs(name)) - splitRXName[i] = val; - } + // --------------------------------------------------------------------------- + // [Icom] + // --------------------------------------------------------------------------- + } else if (section == SECTION_ICOM) { + if (key == "Port") + m_icomPort = val; + +#if defined(MQTT) + // --------------------------------------------------------------------------- + // [MQTT] + // --------------------------------------------------------------------------- + } else if (section == SECTION_MQTT) { + if (key == "Host") + m_mqttHost = val; + else if (key == "Port") + m_mqttPort = (unsigned int)std::stoul(val); + else if (key == "Auth") + m_mqttAuth = std::stol(val) == 1L; + else if (key == "Username") + m_mqttUsername = val; + else if (key == "Password") + m_mqttPassword = val; + else if (key == "Keepalive") + m_mqttKeepalive = (unsigned int)std::stoul(val); + else if (key == "Name") + m_mqttName = val; +#endif } } - file.Close(); - - // Move the array of wxStrings into the wxArrayString + // Move the indexed arrays into vectors. for (unsigned int i = 0U; i < SPLIT_TX_COUNT; i++) - m_splitTXNames.Add(splitTXName[i]); + m_splitTXNames.push_back(splitTXName[i]); for (unsigned int i = 0U; i < SPLIT_RX_COUNT; i++) - m_splitRXNames.Add(splitRXName[i]); - - delete[] splitTXName; - delete[] splitRXName; + m_splitRXNames.push_back(splitRXName[i]); } CDStarRepeaterConfig::~CDStarRepeaterConfig() { } -#endif +// --------------------------------------------------------------------------- +// Accessor implementations +// --------------------------------------------------------------------------- -void CDStarRepeaterConfig::getCallsign(wxString& callsign, wxString& gateway, DSTAR_MODE& mode, ACK_TYPE& ack, bool& restriction, bool& rpt1Validation, bool& dtmfBlanking, bool& errorReply) const +void CDStarRepeaterConfig::getCallsign(std::string& callsign, std::string& gateway, DSTAR_MODE& mode, ACK_TYPE& ack, bool& restriction, bool& rpt1Validation, bool& dtmfBlanking, bool& errorReply) const { callsign = m_callsign; gateway = m_gateway; @@ -1264,19 +836,21 @@ void CDStarRepeaterConfig::getCallsign(wxString& callsign, wxString& gateway, DS errorReply = m_errorReply; } -void CDStarRepeaterConfig::setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply) +void CDStarRepeaterConfig::getLog(std::string& filePath, unsigned int& fileLevel, unsigned int& displayLevel, unsigned int& mqttLevel) const +{ + filePath = m_logFilePath; + fileLevel = m_logFileLevel; + displayLevel = m_logDisplayLevel; + mqttLevel = m_logMQTTLevel; +} + +void CDStarRepeaterConfig::getPaths(std::string& dataDir, std::string& audioDir) const { - m_callsign = callsign; - m_gateway = gateway; - m_mode = mode; - m_ack = ack; - m_restriction = restriction; - m_rpt1Validation = rpt1Validation; - m_dtmfBlanking = dtmfBlanking; - m_errorReply = errorReply; + dataDir = m_dataDir; + audioDir = m_audioDir; } -void CDStarRepeaterConfig::getNetwork(wxString& gatewayAddress, unsigned int& gatewayPort, wxString& localAddress, unsigned int& localPort, wxString& name) const +void CDStarRepeaterConfig::getNetwork(std::string& gatewayAddress, unsigned int& gatewayPort, std::string& localAddress, unsigned int& localPort, std::string& name) const { gatewayAddress = m_gatewayAddress; gatewayPort = m_gatewayPort; @@ -1285,38 +859,18 @@ void CDStarRepeaterConfig::getNetwork(wxString& gatewayAddress, unsigned int& ga name = m_networkName; } -void CDStarRepeaterConfig::setNetwork(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name) -{ - m_gatewayAddress = gatewayAddress; - m_gatewayPort = gatewayPort; - m_localAddress = localAddress; - m_localPort = localPort; - m_networkName = name; -} - -void CDStarRepeaterConfig::getModem(wxString& type) const +void CDStarRepeaterConfig::getModem(std::string& type) const { type = m_modemType; } -void CDStarRepeaterConfig::setModem(const wxString& type) -{ - m_modemType = type; -} - void CDStarRepeaterConfig::getTimes(unsigned int& timeout, unsigned int& ackTime) const { timeout = m_timeout; ackTime = m_ackTime; } -void CDStarRepeaterConfig::setTimes(unsigned int timeout, unsigned int ackTime) -{ - m_timeout = timeout; - m_ackTime = ackTime; -} - -void CDStarRepeaterConfig::getBeacon(unsigned int& time, wxString& text, bool& voice, TEXT_LANG& language) const +void CDStarRepeaterConfig::getBeacon(unsigned int& time, std::string& text, bool& voice, TEXT_LANG& language) const { time = m_beaconTime; text = m_beaconText; @@ -1324,15 +878,7 @@ void CDStarRepeaterConfig::getBeacon(unsigned int& time, wxString& text, bool& v language = m_language; } -void CDStarRepeaterConfig::setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language) -{ - m_beaconTime = time; - m_beaconText = text; - m_beaconVoice = voice; - m_language = language; -} - -void CDStarRepeaterConfig::getAnnouncement(bool& enabled, unsigned int& time, wxString& recordRPT1, wxString& recordRPT2, wxString& deleteRPT1, wxString& deleteRPT2) const +void CDStarRepeaterConfig::getAnnouncement(bool& enabled, unsigned int& time, std::string& recordRPT1, std::string& recordRPT2, std::string& deleteRPT1, std::string& deleteRPT2) const { enabled = m_announcementEnabled; time = m_announcementTime; @@ -1342,17 +888,7 @@ void CDStarRepeaterConfig::getAnnouncement(bool& enabled, unsigned int& time, wx deleteRPT2 = m_announcementDeleteRPT2; } -void CDStarRepeaterConfig::setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2) -{ - m_announcementEnabled = enabled; - m_announcementTime = time; - m_announcementRecordRPT1 = recordRPT1; - m_announcementRecordRPT2 = recordRPT2; - m_announcementDeleteRPT1 = deleteRPT1; - m_announcementDeleteRPT2 = deleteRPT2; -} - -void CDStarRepeaterConfig::getControl(bool& enabled, wxString& rpt1Callsign, wxString& rpt2Callsign, wxString& shutdown, wxString& startup, wxString& status1, wxString& status2, wxString& status3, wxString& status4, wxString& status5, wxString& command1, wxString& command1Line, wxString& command2, wxString& command2Line, wxString& command3, wxString& command3Line, wxString& command4, wxString& command4Line, wxString& command5, wxString& command5Line, wxString& command6, wxString& command6Line, wxString& output1, wxString& output2, wxString& output3, wxString& output4) const +void CDStarRepeaterConfig::getControl(bool& enabled, std::string& rpt1Callsign, std::string& rpt2Callsign, std::string& shutdown, std::string& startup, std::string& status1, std::string& status2, std::string& status3, std::string& status4, std::string& status5, std::string& command1, std::string& command1Line, std::string& command2, std::string& command2Line, std::string& command3, std::string& command3Line, std::string& command4, std::string& command4Line, std::string& command5, std::string& command5Line, std::string& command6, std::string& command6Line, std::string& output1, std::string& output2, std::string& output3, std::string& output4) const { enabled = m_controlEnabled; rpt1Callsign = m_controlRpt1Callsign; @@ -1385,37 +921,7 @@ void CDStarRepeaterConfig::getControl(bool& enabled, wxString& rpt1Callsign, wxS output4 = m_controlOutput4; } -void CDStarRepeaterConfig::setControl(bool enabled, const wxString& rpt1Callsign, const wxString& rpt2Callsign, const wxString& shutdown, const wxString& startup, const wxString& status1, const wxString& status2, const wxString& status3, const wxString& status4, const wxString& status5, const wxString& command1, const wxString& command1Line, const wxString& command2, const wxString& command2Line, const wxString& command3, const wxString& command3Line, const wxString& command4, const wxString& command4Line, const wxString& command5, const wxString& command5Line, const wxString& command6, const wxString& command6Line, const wxString& output1, const wxString& output2, const wxString& output3, const wxString& output4) -{ - m_controlEnabled = enabled; - m_controlRpt1Callsign = rpt1Callsign; - m_controlRpt2Callsign = rpt2Callsign; - m_controlShutdown = shutdown; - m_controlStartup = startup; - m_controlStatus1 = status1; - m_controlStatus2 = status2; - m_controlStatus3 = status3; - m_controlStatus4 = status4; - m_controlStatus5 = status5; - m_controlCommand1 = command1; - m_controlCommand1Line = command1Line; - m_controlCommand2 = command2; - m_controlCommand2Line = command2Line; - m_controlCommand3 = command3; - m_controlCommand3Line = command3Line; - m_controlCommand4 = command4; - m_controlCommand4Line = command4Line; - m_controlCommand5 = command5; - m_controlCommand5Line = command5Line; - m_controlCommand6 = command6; - m_controlCommand6Line = command6Line; - m_controlOutput1 = output1; - m_controlOutput2 = output2; - m_controlOutput3 = output3; - m_controlOutput4 = output4; -} - -void CDStarRepeaterConfig::getController(wxString& type, unsigned int& serialConfig, bool& pttInvert, unsigned int& activeHangTime) const +void CDStarRepeaterConfig::getController(std::string& type, unsigned int& serialConfig, bool& pttInvert, unsigned int& activeHangTime) const { type = m_controllerType; serialConfig = m_serialConfig; @@ -1423,14 +929,6 @@ void CDStarRepeaterConfig::getController(wxString& type, unsigned int& serialCon activeHangTime = m_activeHangTime; } -void CDStarRepeaterConfig::setController(const wxString& type, unsigned int serialConfig, bool pttInvert, unsigned int activeHangTime) -{ - m_controllerType = type; - m_serialConfig = serialConfig; - m_pttInvert = pttInvert; - m_activeHangTime = activeHangTime; -} - void CDStarRepeaterConfig::getOutputs(bool& out1, bool& out2, bool& out3, bool& out4) const { out1 = m_output1; @@ -1439,37 +937,27 @@ void CDStarRepeaterConfig::getOutputs(bool& out1, bool& out2, bool& out3, bool& out4 = m_output4; } -void CDStarRepeaterConfig::setOutputs(bool out1, bool out2, bool out3, bool out4) -{ - m_output1 = out1; - m_output2 = out2; - m_output3 = out3; - m_output4 = out4; -} - void CDStarRepeaterConfig::getLogging(bool& logging) const { logging = m_logging; } -void CDStarRepeaterConfig::setLogging(bool logging) +void CDStarRepeaterConfig::getWhitelist(std::string& file) const { - m_logging = logging; + file = m_whitelistFile; } -void CDStarRepeaterConfig::getPosition(int& x, int& y) const +void CDStarRepeaterConfig::getBlacklist(std::string& file) const { - x = m_x; - y = m_y; + file = m_blacklistFile; } -void CDStarRepeaterConfig::setPosition(int x, int y) +void CDStarRepeaterConfig::getGreylist(std::string& file) const { - m_x = x; - m_y = y; + file = m_greylistFile; } -void CDStarRepeaterConfig::getDVAP(wxString& port, unsigned int& frequency, int& power, int& squelch) const +void CDStarRepeaterConfig::getDVAP(std::string& port, unsigned int& frequency, int& power, int& squelch) const { port = m_dvapPort; frequency = m_dvapFrequency; @@ -1477,27 +965,13 @@ void CDStarRepeaterConfig::getDVAP(wxString& port, unsigned int& frequency, int& squelch = m_dvapSquelch; } -void CDStarRepeaterConfig::setDVAP(const wxString& port, unsigned int frequency, int power, int squelch) -{ - m_dvapPort = port; - m_dvapFrequency = frequency; - m_dvapPower = power; - m_dvapSquelch = squelch; -} - void CDStarRepeaterConfig::getGMSK(USB_INTERFACE& type, unsigned int& address) const { type = m_gmskInterface; address = m_gmskAddress; } -void CDStarRepeaterConfig::setGMSK(USB_INTERFACE type, unsigned int address) -{ - m_gmskInterface = type; - m_gmskAddress = address; -} - -void CDStarRepeaterConfig::getDVRPTR1(wxString& port, bool& rxInvert, bool& txInvert, bool& channel, unsigned int& modLevel, unsigned int& txDelay) const +void CDStarRepeaterConfig::getDVRPTR1(std::string& port, bool& rxInvert, bool& txInvert, bool& channel, unsigned int& modLevel, unsigned int& txDelay) const { port = m_dvrptr1Port; rxInvert = m_dvrptr1RXInvert; @@ -1507,17 +981,7 @@ void CDStarRepeaterConfig::getDVRPTR1(wxString& port, bool& rxInvert, bool& txIn txDelay = m_dvrptr1TXDelay; } -void CDStarRepeaterConfig::setDVRPTR1(const wxString& port, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay) -{ - m_dvrptr1Port = port; - m_dvrptr1RXInvert = rxInvert; - m_dvrptr1TXInvert = txInvert; - m_dvrptr1Channel = channel; - m_dvrptr1ModLevel = modLevel; - m_dvrptr1TXDelay = txDelay; -} - -void CDStarRepeaterConfig::getDVRPTR2(CONNECTION_TYPE& connection, wxString& usbPort, wxString& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const +void CDStarRepeaterConfig::getDVRPTR2(CONNECTION_TYPE& connection, std::string& usbPort, std::string& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const { connection = m_dvrptr2Connection; usbPort = m_dvrptr2USBPort; @@ -1528,18 +992,7 @@ void CDStarRepeaterConfig::getDVRPTR2(CONNECTION_TYPE& connection, wxString& usb txDelay = m_dvrptr2TXDelay; } -void CDStarRepeaterConfig::setDVRPTR2(CONNECTION_TYPE connection, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay) -{ - m_dvrptr2Connection = connection; - m_dvrptr2USBPort = usbPort; - m_dvrptr2Address = address; - m_dvrptr2Port = port; - m_dvrptr2TXInvert = txInvert; - m_dvrptr2ModLevel = modLevel; - m_dvrptr2TXDelay = txDelay; -} - -void CDStarRepeaterConfig::getDVRPTR3(CONNECTION_TYPE& connection, wxString& usbPort, wxString& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const +void CDStarRepeaterConfig::getDVRPTR3(CONNECTION_TYPE& connection, std::string& usbPort, std::string& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const { connection = m_dvrptr3Connection; usbPort = m_dvrptr3USBPort; @@ -1550,18 +1003,7 @@ void CDStarRepeaterConfig::getDVRPTR3(CONNECTION_TYPE& connection, wxString& usb txDelay = m_dvrptr3TXDelay; } -void CDStarRepeaterConfig::setDVRPTR3(CONNECTION_TYPE connection, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay) -{ - m_dvrptr3Connection = connection; - m_dvrptr3USBPort = usbPort; - m_dvrptr3Address = address; - m_dvrptr3Port = port; - m_dvrptr3TXInvert = txInvert; - m_dvrptr3ModLevel = modLevel; - m_dvrptr3TXDelay = txDelay; -} - -void CDStarRepeaterConfig::getDVMEGA(wxString& port, DVMEGA_VARIANT& variant, bool& rxInvert, bool& txInvert, unsigned int& txDelay, unsigned int& rxFrequency, unsigned int& txFrequency, unsigned int& power) const +void CDStarRepeaterConfig::getDVMEGA(std::string& port, DVMEGA_VARIANT& variant, bool& rxInvert, bool& txInvert, unsigned int& txDelay, unsigned int& rxFrequency, unsigned int& txFrequency, unsigned int& power) const { port = m_dvmegaPort; variant = m_dvmegaVariant; @@ -1573,19 +1015,7 @@ void CDStarRepeaterConfig::getDVMEGA(wxString& port, DVMEGA_VARIANT& variant, bo power = m_dvmegaPower; } -void CDStarRepeaterConfig::setDVMEGA(const wxString& port, DVMEGA_VARIANT variant, bool rxInvert, bool txInvert, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power) -{ - m_dvmegaPort = port; - m_dvmegaVariant = variant; - m_dvmegaRXInvert = rxInvert; - m_dvmegaTXInvert = txInvert; - m_dvmegaTXDelay = txDelay; - m_dvmegaRXFrequency = rxFrequency; - m_dvmegaTXFrequency = txFrequency; - m_dvmegaPower = power; -} - -void CDStarRepeaterConfig::getMMDVM(wxString& port, bool& rxInvert, bool& txInvert, bool& pttInvert, unsigned int& txDelay, unsigned int& rxLevel, unsigned int& txLevel) const +void CDStarRepeaterConfig::getMMDVM(std::string& port, bool& rxInvert, bool& txInvert, bool& pttInvert, unsigned int& txDelay, unsigned int& rxLevel, unsigned int& txLevel) const { port = m_mmdvmPort; rxInvert = m_mmdvmRXInvert; @@ -1596,18 +1026,7 @@ void CDStarRepeaterConfig::getMMDVM(wxString& port, bool& rxInvert, bool& txInve txLevel = m_mmdvmTXLevel; } -void CDStarRepeaterConfig::setMMDVM(const wxString& port, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel) -{ - m_mmdvmPort = port; - m_mmdvmRXInvert = rxInvert; - m_mmdvmTXInvert = txInvert; - m_mmdvmPTTInvert = pttInvert; - m_mmdvmTXDelay = txDelay; - m_mmdvmRXLevel = rxLevel; - m_mmdvmTXLevel = txLevel; -} - -void CDStarRepeaterConfig::getSoundCard(wxString& rxDevice, wxString& txDevice, bool& rxInvert, bool& txInvert, wxFloat32& rxLevel, wxFloat32& txLevel, unsigned int& txDelay, unsigned int& txTail) const +void CDStarRepeaterConfig::getSoundCard(std::string& rxDevice, std::string& txDevice, bool& rxInvert, bool& txInvert, float& rxLevel, float& txLevel, unsigned int& txDelay, unsigned int& txTail) const { rxDevice = m_soundCardRXDevice; txDevice = m_soundCardTXDevice; @@ -1619,19 +1038,7 @@ void CDStarRepeaterConfig::getSoundCard(wxString& rxDevice, wxString& txDevice, txTail = m_soundCardTXTail; } -void CDStarRepeaterConfig::setSoundCard(const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail) -{ - m_soundCardRXDevice = rxDevice; - m_soundCardTXDevice = txDevice; - m_soundCardRXInvert = rxInvert; - m_soundCardTXInvert = txInvert; - m_soundCardRXLevel = rxLevel; - m_soundCardTXLevel = txLevel; - m_soundCardTXDelay = txDelay; - m_soundCardTXTail = txTail; -} - -void CDStarRepeaterConfig::getSplit(wxString& localAddress, unsigned int& localPort, wxArrayString& transmitterNames, wxArrayString& receiverNames, unsigned int& timeout) const +void CDStarRepeaterConfig::getSplit(std::string& localAddress, unsigned int& localPort, std::vector& transmitterNames, std::vector& receiverNames, unsigned int& timeout) const { localAddress = m_splitLocalAddress; localPort = m_splitLocalPort; @@ -1640,27 +1047,13 @@ void CDStarRepeaterConfig::getSplit(wxString& localAddress, unsigned int& localP timeout = m_splitTimeout; } -void CDStarRepeaterConfig::setSplit(const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout) -{ - m_splitLocalAddress = localAddress; - m_splitLocalPort = localPort; - m_splitTXNames = transmitterNames; - m_splitRXNames = receiverNames; - m_splitTimeout = timeout; -} - -void CDStarRepeaterConfig::getIcom(wxString& port) const +void CDStarRepeaterConfig::getIcom(std::string& port) const { port = m_icomPort; } -void CDStarRepeaterConfig::setIcom(const wxString& port) -{ - m_icomPort = port; -} - #if defined(MQTT) -void CDStarRepeaterConfig::getMQTT(wxString& host, unsigned int& port, bool& auth, wxString& username, wxString& password, unsigned int& keepalive, wxString& name) const +void CDStarRepeaterConfig::getMQTT(std::string& host, unsigned int& port, bool& auth, std::string& username, std::string& password, unsigned int& keepalive, std::string& name) const { host = m_mqttHost; port = m_mqttPort; @@ -1670,347 +1063,4 @@ void CDStarRepeaterConfig::getMQTT(wxString& host, unsigned int& port, bool& aut keepalive = m_mqttKeepalive; name = m_mqttName; } - -void CDStarRepeaterConfig::setMQTT(const wxString& host, unsigned int port, bool auth, const wxString& username, const wxString& password, unsigned int keepalive, const wxString& name) -{ - m_mqttHost = host; - m_mqttPort = port; - m_mqttAuth = auth; - m_mqttUsername = username; - m_mqttPassword = password; - m_mqttKeepalive = keepalive; - m_mqttName = name; -} #endif - -bool CDStarRepeaterConfig::write() -{ -#if defined(__WINDOWS__) - m_config->Write(m_name + KEY_CALLSIGN, m_callsign); - m_config->Write(m_name + KEY_GATEWAY, m_gateway); - m_config->Write(m_name + KEY_MODE, long(m_mode)); - m_config->Write(m_name + KEY_ACK, long(m_ack)); - m_config->Write(m_name + KEY_RESTRICTION, m_restriction); - m_config->Write(m_name + KEY_RPT1_VALIDATION, m_rpt1Validation); - m_config->Write(m_name + KEY_DTMF_BLANKING, m_dtmfBlanking); - m_config->Write(m_name + KEY_ERROR_REPLY, m_errorReply); - m_config->Write(m_name + KEY_GATEWAY_ADDRESS, m_gatewayAddress); - m_config->Write(m_name + KEY_GATEWAY_PORT, long(m_gatewayPort)); - m_config->Write(m_name + KEY_LOCAL_ADDRESS, m_localAddress); - m_config->Write(m_name + KEY_LOCAL_PORT, long(m_localPort)); - m_config->Write(m_name + KEY_NETWORK_NAME, m_networkName); - m_config->Write(m_name + KEY_MODEM_TYPE, m_modemType); - m_config->Write(m_name + KEY_TIMEOUT, long(m_timeout)); - m_config->Write(m_name + KEY_ACK_TIME, long(m_ackTime)); - m_config->Write(m_name + KEY_BEACON_TIME, long(m_beaconTime)); - m_config->Write(m_name + KEY_BEACON_TEXT, m_beaconText); - m_config->Write(m_name + KEY_BEACON_VOICE, m_beaconVoice); - m_config->Write(m_name + KEY_LANGUAGE, long(m_language)); - m_config->Write(m_name + KEY_ANNOUNCEMENT_ENABLED, m_announcementEnabled); - m_config->Write(m_name + KEY_ANNOUNCEMENT_TIME, long(m_announcementTime)); - m_config->Write(m_name + KEY_ANNOUNCEMENT_RECORD_RPT1, m_announcementRecordRPT1); - m_config->Write(m_name + KEY_ANNOUNCEMENT_RECORD_RPT2, m_announcementRecordRPT2); - m_config->Write(m_name + KEY_ANNOUNCEMENT_DELETE_RPT1, m_announcementDeleteRPT1); - m_config->Write(m_name + KEY_ANNOUNCEMENT_DELETE_RPT2, m_announcementDeleteRPT2); - m_config->Write(m_name + KEY_CONTROL_ENABLED, m_controlEnabled); - m_config->Write(m_name + KEY_CONTROL_RPT1, m_controlRpt1Callsign); - m_config->Write(m_name + KEY_CONTROL_RPT2, m_controlRpt2Callsign); - m_config->Write(m_name + KEY_CONTROL_SHUTDOWN, m_controlShutdown); - m_config->Write(m_name + KEY_CONTROL_STARTUP, m_controlStartup); - m_config->Write(m_name + KEY_CONTROL_STATUS1, m_controlStatus1); - m_config->Write(m_name + KEY_CONTROL_STATUS2, m_controlStatus2); - m_config->Write(m_name + KEY_CONTROL_STATUS3, m_controlStatus3); - m_config->Write(m_name + KEY_CONTROL_STATUS4, m_controlStatus4); - m_config->Write(m_name + KEY_CONTROL_STATUS5, m_controlStatus5); - m_config->Write(m_name + KEY_CONTROL_COMMAND1, m_controlCommand1); - m_config->Write(m_name + KEY_CONTROL_COMMAND1_LINE, m_controlCommand1Line); - m_config->Write(m_name + KEY_CONTROL_COMMAND2, m_controlCommand2); - m_config->Write(m_name + KEY_CONTROL_COMMAND2_LINE, m_controlCommand2Line); - m_config->Write(m_name + KEY_CONTROL_COMMAND3, m_controlCommand3); - m_config->Write(m_name + KEY_CONTROL_COMMAND3_LINE, m_controlCommand3Line); - m_config->Write(m_name + KEY_CONTROL_COMMAND4, m_controlCommand4); - m_config->Write(m_name + KEY_CONTROL_COMMAND4_LINE, m_controlCommand4Line); - m_config->Write(m_name + KEY_CONTROL_COMMAND5, m_controlCommand5); - m_config->Write(m_name + KEY_CONTROL_COMMAND5_LINE, m_controlCommand5Line); - m_config->Write(m_name + KEY_CONTROL_COMMAND6, m_controlCommand6); - m_config->Write(m_name + KEY_CONTROL_COMMAND6_LINE, m_controlCommand6Line); - m_config->Write(m_name + KEY_CONTROL_OUTPUT1, m_controlOutput1); - m_config->Write(m_name + KEY_CONTROL_OUTPUT2, m_controlOutput2); - m_config->Write(m_name + KEY_CONTROL_OUTPUT3, m_controlOutput3); - m_config->Write(m_name + KEY_CONTROL_OUTPUT4, m_controlOutput4); - m_config->Write(m_name + KEY_CONTROLLER_TYPE, m_controllerType); - m_config->Write(m_name + KEY_SERIAL_CONFIG, long(m_serialConfig)); - m_config->Write(m_name + KEY_PTT_INVERT, m_pttInvert); - m_config->Write(m_name + KEY_ACTIVE_HANG_TIME, long(m_activeHangTime)); - m_config->Write(m_name + KEY_OUTPUT1, m_output1); - m_config->Write(m_name + KEY_OUTPUT2, m_output2); - m_config->Write(m_name + KEY_OUTPUT3, m_output3); - m_config->Write(m_name + KEY_OUTPUT4, m_output4); - m_config->Write(m_name + KEY_LOGGING, m_logging); - m_config->Write(m_name + KEY_WINDOW_X, long(m_x)); - m_config->Write(m_name + KEY_WINDOW_Y, long(m_y)); - - m_config->Write(m_name + KEY_DVAP_PORT, m_dvapPort); - m_config->Write(m_name + KEY_DVAP_FREQUENCY, long(m_dvapFrequency)); - m_config->Write(m_name + KEY_DVAP_POWER, long(m_dvapPower)); - m_config->Write(m_name + KEY_DVAP_SQUELCH, long(m_dvapSquelch)); - - m_config->Write(m_name + KEY_GMSK_INTERFACE, long(m_gmskInterface)); - m_config->Write(m_name + KEY_GMSK_ADDRESS, long(m_gmskAddress)); - - m_config->Write(m_name + KEY_DVRPTR1_PORT, m_dvrptr1Port); - m_config->Write(m_name + KEY_DVRPTR1_RXINVERT, m_dvrptr1RXInvert); - m_config->Write(m_name + KEY_DVRPTR1_TXINVERT, m_dvrptr1TXInvert); - m_config->Write(m_name + KEY_DVRPTR1_CHANNEL, m_dvrptr1Channel); - m_config->Write(m_name + KEY_DVRPTR1_MODLEVEL, long(m_dvrptr1ModLevel)); - m_config->Write(m_name + KEY_DVRPTR1_TXDELAY, long(m_dvrptr1TXDelay)); - - m_config->Write(m_name + KEY_DVRPTR2_CONNECTION, long(m_dvrptr2Connection)); - m_config->Write(m_name + KEY_DVRPTR2_USBPORT, m_dvrptr2USBPort); - m_config->Write(m_name + KEY_DVRPTR2_ADDRESS, m_dvrptr2Address); - m_config->Write(m_name + KEY_DVRPTR2_PORT, long(m_dvrptr2Port)); - m_config->Write(m_name + KEY_DVRPTR2_TXINVERT, m_dvrptr2TXInvert); - m_config->Write(m_name + KEY_DVRPTR2_MODLEVEL, long(m_dvrptr2ModLevel)); - m_config->Write(m_name + KEY_DVRPTR2_TXDELAY, long(m_dvrptr2TXDelay)); - - m_config->Write(m_name + KEY_DVRPTR3_CONNECTION, long(m_dvrptr3Connection)); - m_config->Write(m_name + KEY_DVRPTR3_USBPORT, m_dvrptr3USBPort); - m_config->Write(m_name + KEY_DVRPTR3_ADDRESS, m_dvrptr3Address); - m_config->Write(m_name + KEY_DVRPTR3_PORT, long(m_dvrptr3Port)); - m_config->Write(m_name + KEY_DVRPTR3_TXINVERT, m_dvrptr3TXInvert); - m_config->Write(m_name + KEY_DVRPTR3_MODLEVEL, long(m_dvrptr3ModLevel)); - m_config->Write(m_name + KEY_DVRPTR3_TXDELAY, long(m_dvrptr3TXDelay)); - - m_config->Write(m_name + KEY_DVMEGA_PORT, m_dvmegaPort); - m_config->Write(m_name + KEY_DVMEGA_VARIANT, long(m_dvmegaVariant)); - m_config->Write(m_name + KEY_DVMEGA_RXINVERT, m_dvmegaRXInvert); - m_config->Write(m_name + KEY_DVMEGA_TXINVERT, m_dvmegaTXInvert); - m_config->Write(m_name + KEY_DVMEGA_TXDELAY, long(m_dvmegaTXDelay)); - m_config->Write(m_name + KEY_DVMEGA_RXFREQUENCY, long(m_dvmegaRXFrequency)); - m_config->Write(m_name + KEY_DVMEGA_TXFREQUENCY, long(m_dvmegaTXFrequency)); - m_config->Write(m_name + KEY_DVMEGA_POWER, long(m_dvmegaPower)); - - m_config->Write(m_name + KEY_MMDVM_PORT, m_mmdvmPort); - m_config->Write(m_name + KEY_MMDVM_RXINVERT, m_mmdvmRXInvert); - m_config->Write(m_name + KEY_MMDVM_TXINVERT, m_mmdvmTXInvert); - m_config->Write(m_name + KEY_MMDVM_PTTINVERT, m_mmdvmPTTInvert); - m_config->Write(m_name + KEY_MMDVM_TXDELAY, long(m_mmdvmTXDelay)); - m_config->Write(m_name + KEY_MMDVM_RXLEVEL, long(m_mmdvmRXLevel)); - m_config->Write(m_name + KEY_MMDVM_TXLEVEL, long(m_mmdvmTXLevel)); - - m_config->Write(m_name + KEY_SOUNDCARD_RXDEVICE, m_soundCardRXDevice); - m_config->Write(m_name + KEY_SOUNDCARD_TXDEVICE, m_soundCardTXDevice); - m_config->Write(m_name + KEY_SOUNDCARD_RXINVERT, m_soundCardRXInvert); - m_config->Write(m_name + KEY_SOUNDCARD_TXINVERT, m_soundCardTXInvert); - m_config->Write(m_name + KEY_SOUNDCARD_RXLEVEL, double(m_soundCardRXLevel)); - m_config->Write(m_name + KEY_SOUNDCARD_TXLEVEL, double(m_soundCardTXLevel)); - m_config->Write(m_name + KEY_SOUNDCARD_TXDELAY, long(m_soundCardTXDelay)); - m_config->Write(m_name + KEY_SOUNDCARD_TXTAIL, long(m_soundCardTXTail)); - - m_config->Write(m_name + KEY_ICOM_PORT, m_icomPort); - - m_config->Write(m_name + KEY_SPLIT_LOCALADDRESS, m_splitLocalAddress); - m_config->Write(m_name + KEY_SPLIT_LOCALPORT, long(m_splitLocalPort)); - - for (unsigned int i = 0U; i < m_splitTXNames.GetCount(); i++) { - wxString name; - name.Printf(wxT("%s%u"), KEY_SPLIT_TXNAME.c_str(), i); - - m_config->Write(m_name + name, m_splitTXNames.Item(i)); - } - - for (unsigned int i = 0U; i < m_splitRXNames.GetCount(); i++) { - wxString name; - name.Printf(wxT("%s%u"), KEY_SPLIT_RXNAME.c_str(), i); - - m_config->Write(m_name + name, m_splitRXNames.Item(i)); - } - - m_config->Write(m_name + KEY_SPLIT_TIMEOUT, long(m_splitTimeout)); - - m_config->Flush(); -#endif - - wxTextFile file(m_fileName.GetFullPath()); - - bool exists = file.Exists(); - if (exists) { - bool ret = file.Open(); - if (!ret) { - wxLogError(wxT("Cannot open the config file - %s"), m_fileName.GetFullPath().c_str()); - return false; - } - - // Remove the existing file entries - file.Clear(); - } else { - bool ret = file.Create(); - if (!ret) { - wxLogError(wxT("Cannot create the config file - %s"), m_fileName.GetFullPath().c_str()); - return false; - } - } - - wxString buffer; - buffer.Printf(wxT("%s=%s"), KEY_CALLSIGN.c_str(), m_callsign.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_GATEWAY.c_str(), m_gateway.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_MODE.c_str(), int(m_mode)); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_ACK.c_str(), int(m_ack)); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_RESTRICTION.c_str(), m_restriction ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_RPT1_VALIDATION.c_str(), m_rpt1Validation ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_DTMF_BLANKING.c_str(), m_dtmfBlanking ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_ERROR_REPLY.c_str(), m_errorReply ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_GATEWAY_ADDRESS.c_str(), m_gatewayAddress.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_GATEWAY_PORT.c_str(), m_gatewayPort); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_LOCAL_ADDRESS.c_str(), m_localAddress.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_LOCAL_PORT.c_str(), m_localPort); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_NETWORK_NAME.c_str(), m_networkName.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_MODEM_TYPE.c_str(), m_modemType.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_TIMEOUT.c_str(), m_timeout); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_ACK_TIME.c_str(), m_ackTime); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_BEACON_TIME.c_str(), m_beaconTime); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_BEACON_TEXT.c_str(), m_beaconText.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_BEACON_VOICE.c_str(), m_beaconVoice ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_LANGUAGE.c_str(), int(m_language)); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_ANNOUNCEMENT_ENABLED.c_str(), m_announcementEnabled ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_ANNOUNCEMENT_TIME.c_str(), m_announcementTime); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_ANNOUNCEMENT_RECORD_RPT1.c_str(), m_announcementRecordRPT1.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_ANNOUNCEMENT_RECORD_RPT2.c_str(), m_announcementRecordRPT2.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_ANNOUNCEMENT_DELETE_RPT1.c_str(), m_announcementDeleteRPT1.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_ANNOUNCEMENT_DELETE_RPT2.c_str(), m_announcementDeleteRPT2.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_CONTROL_ENABLED.c_str(), m_controlEnabled ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_RPT1.c_str(), m_controlRpt1Callsign.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_RPT2.c_str(), m_controlRpt2Callsign.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_SHUTDOWN.c_str(), m_controlShutdown.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STARTUP.c_str(), m_controlStartup.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STATUS1.c_str(), m_controlStatus1.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STATUS2.c_str(), m_controlStatus2.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STATUS3.c_str(), m_controlStatus3.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STATUS4.c_str(), m_controlStatus4.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_STATUS5.c_str(), m_controlStatus5.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND1.c_str(), m_controlCommand1.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND1_LINE.c_str(), m_controlCommand1Line.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND2.c_str(), m_controlCommand2.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND2_LINE.c_str(), m_controlCommand2Line.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND3.c_str(), m_controlCommand3.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND3_LINE.c_str(), m_controlCommand3Line.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND4.c_str(), m_controlCommand4.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND4_LINE.c_str(), m_controlCommand4Line.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND5.c_str(), m_controlCommand5.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND5_LINE.c_str(), m_controlCommand5Line.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND6.c_str(), m_controlCommand6.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_COMMAND6_LINE.c_str(), m_controlCommand6Line.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_OUTPUT1.c_str(), m_controlOutput1.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_OUTPUT2.c_str(), m_controlOutput2.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_OUTPUT3.c_str(), m_controlOutput3.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROL_OUTPUT4.c_str(), m_controlOutput4.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_CONTROLLER_TYPE.c_str(), m_controllerType.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_SERIAL_CONFIG.c_str(), m_serialConfig); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_PTT_INVERT.c_str(), m_pttInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_ACTIVE_HANG_TIME.c_str(), m_activeHangTime); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_OUTPUT1.c_str(), m_output1 ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_OUTPUT2.c_str(), m_output2 ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_OUTPUT3.c_str(), m_output3 ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_OUTPUT4.c_str(), m_output4 ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_LOGGING.c_str(), m_logging ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_WINDOW_X.c_str(), m_x); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_WINDOW_Y.c_str(), m_y); file.AddLine(buffer); - - buffer.Printf(wxT("%s=%s"), KEY_DVAP_PORT.c_str(), m_dvapPort.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVAP_FREQUENCY.c_str(), m_dvapFrequency); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_DVAP_POWER.c_str(), m_dvapPower); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_DVAP_SQUELCH.c_str(), m_dvapSquelch); file.AddLine(buffer); - - buffer.Printf(wxT("%s=%u"), KEY_GMSK_ADDRESS.c_str(), m_gmskAddress); file.AddLine(buffer); - - buffer.Printf(wxT("%s=%s"), KEY_DVRPTR1_PORT.c_str(), m_dvrptr1Port.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_DVRPTR1_RXINVERT.c_str(), m_dvrptr1RXInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_DVRPTR1_TXINVERT.c_str(), m_dvrptr1TXInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_DVRPTR1_CHANNEL.c_str(), m_dvrptr1Channel ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVRPTR1_MODLEVEL.c_str(), m_dvrptr1ModLevel); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVRPTR1_TXDELAY.c_str(), m_dvrptr1TXDelay); file.AddLine(buffer); - - buffer.Printf(wxT("%s=%d"), KEY_DVRPTR2_CONNECTION.c_str(), int(m_dvrptr2Connection)); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_DVRPTR2_USBPORT.c_str(), m_dvrptr2USBPort.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_DVRPTR2_ADDRESS.c_str(), m_dvrptr2Address.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVRPTR2_PORT.c_str(), m_dvrptr2Port); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_DVRPTR2_TXINVERT.c_str(), m_dvrptr2TXInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVRPTR2_MODLEVEL.c_str(), m_dvrptr2ModLevel); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVRPTR2_TXDELAY.c_str(), m_dvrptr2TXDelay); file.AddLine(buffer); - - buffer.Printf(wxT("%s=%d"), KEY_DVRPTR3_CONNECTION.c_str(), int(m_dvrptr3Connection)); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_DVRPTR3_USBPORT.c_str(), m_dvrptr3USBPort.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_DVRPTR3_ADDRESS.c_str(), m_dvrptr3Address.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVRPTR3_PORT.c_str(), m_dvrptr3Port); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_DVRPTR3_TXINVERT.c_str(), m_dvrptr3TXInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVRPTR3_MODLEVEL.c_str(), m_dvrptr3ModLevel); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVRPTR3_TXDELAY.c_str(), m_dvrptr3TXDelay); file.AddLine(buffer); - - buffer.Printf(wxT("%s=%s"), KEY_DVMEGA_PORT.c_str(), m_dvmegaPort.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_DVMEGA_VARIANT.c_str(), int(m_dvmegaVariant)); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_DVMEGA_RXINVERT.c_str(), m_dvmegaRXInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_DVMEGA_TXINVERT.c_str(), m_dvmegaTXInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVMEGA_TXDELAY.c_str(), m_dvmegaTXDelay); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVMEGA_RXFREQUENCY.c_str(), m_dvmegaRXFrequency); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVMEGA_TXFREQUENCY.c_str(), m_dvmegaTXFrequency); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_DVMEGA_POWER.c_str(), m_dvmegaPower); file.AddLine(buffer); - - buffer.Printf(wxT("%s=%s"), KEY_MMDVM_PORT.c_str(), m_mmdvmPort.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_MMDVM_RXINVERT.c_str(), m_mmdvmRXInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_MMDVM_TXINVERT.c_str(), m_mmdvmTXInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_MMDVM_PTTINVERT.c_str(), m_mmdvmPTTInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_MMDVM_TXDELAY.c_str(), m_mmdvmTXDelay); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_MMDVM_RXLEVEL.c_str(), m_mmdvmRXLevel); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_MMDVM_TXLEVEL.c_str(), m_mmdvmTXLevel); file.AddLine(buffer); - - buffer.Printf(wxT("%s=%s"), KEY_SOUNDCARD_RXDEVICE.c_str(), m_soundCardRXDevice.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_SOUNDCARD_TXDEVICE.c_str(), m_soundCardTXDevice.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_SOUNDCARD_RXINVERT.c_str(), m_soundCardRXInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_SOUNDCARD_TXINVERT.c_str(), m_soundCardTXInvert ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%.4f"), KEY_SOUNDCARD_RXLEVEL.c_str(), m_soundCardRXLevel); file.AddLine(buffer); - buffer.Printf(wxT("%s=%.4f"), KEY_SOUNDCARD_TXLEVEL.c_str(), m_soundCardTXLevel); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_SOUNDCARD_TXDELAY.c_str(), m_soundCardTXDelay); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_SOUNDCARD_TXTAIL.c_str(), m_soundCardTXTail); file.AddLine(buffer); - - buffer.Printf(wxT("%s=%s"), KEY_ICOM_PORT.c_str(), m_icomPort.c_str()); file.AddLine(buffer); - -#if defined(MQTT) - buffer.Printf(wxT("%s=%s"), KEY_MQTT_HOST.c_str(), m_mqttHost.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_MQTT_PORT.c_str(), m_mqttPort); file.AddLine(buffer); - buffer.Printf(wxT("%s=%d"), KEY_MQTT_AUTH.c_str(), m_mqttAuth ? 1 : 0); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_MQTT_USERNAME.c_str(), m_mqttUsername.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_MQTT_PASSWORD.c_str(), m_mqttPassword.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_MQTT_KEEPALIVE.c_str(), m_mqttKeepalive); file.AddLine(buffer); - buffer.Printf(wxT("%s=%s"), KEY_MQTT_NAME.c_str(), m_mqttName.c_str()); file.AddLine(buffer); -#endif - - buffer.Printf(wxT("%s=%s"), KEY_SPLIT_LOCALADDRESS.c_str(), m_splitLocalAddress.c_str()); file.AddLine(buffer); - buffer.Printf(wxT("%s=%u"), KEY_SPLIT_LOCALPORT.c_str(), m_splitLocalPort); file.AddLine(buffer); - - for (unsigned int i = 0U; i < m_splitTXNames.GetCount(); i++) { - wxString name; - name.Printf(wxT("%s%u"), KEY_SPLIT_TXNAME.c_str(), i); - buffer.Printf(wxT("%s=%s"), name.c_str(), m_splitTXNames.Item(i).c_str()); - file.AddLine(buffer); - } - - for (unsigned int i = 0U; i < m_splitRXNames.GetCount(); i++) { - wxString name; - name.Printf(wxT("%s%u"), KEY_SPLIT_RXNAME.c_str(), i); - buffer.Printf(wxT("%s=%s"), name.c_str(), m_splitRXNames.Item(i).c_str()); - file.AddLine(buffer); - } - - buffer.Printf(wxT("%s=%u"), KEY_SPLIT_TIMEOUT.c_str(), m_splitTimeout); file.AddLine(buffer); - - bool ret = file.Write(); - if (!ret) { - file.Close(); - wxLogError(wxT("Cannot write the config file - %s"), m_fileName.GetFullPath().c_str()); - return false; - } - - file.Close(); - - return true; -} diff --git a/Common/DStarRepeaterConfig.h b/Common/DStarRepeaterConfig.h index 4154e2a..e96242b 100644 --- a/Common/DStarRepeaterConfig.h +++ b/Common/DStarRepeaterConfig.h @@ -21,198 +21,248 @@ #include "DStarDefines.h" -#include -#include -#include +#include +#include +#include +/* + * Parses the INI-style repeater configuration file. + * + * File format (MMDVMHost compatible): + * [Section] headers + * Key=Value pairs + * # comment lines + * + * The constructor takes the full path to the config file and throws + * std::runtime_error if the file does not exist or cannot be opened. + * There is no write-back facility; this class is read-only. + * + * Settings are grouped into logical sections exposed via typed getters. + * Every getter reads all fields for that group atomically (all by + * out-reference). + */ class CDStarRepeaterConfig { public: -#if defined(__WINDOWS__) - CDStarRepeaterConfig(wxConfigBase* config, const wxString& dir, const wxString& configName, const wxString& name); -#else - CDStarRepeaterConfig(const wxString& dir, const wxString& configName, const wxString& name, const bool mustExist=false); -#endif + /* + * Opens and parses filePath. Throws std::runtime_error if the file + * does not exist or cannot be opened. All members are initialised to + * built-in defaults before parsing, so any key absent from the file + * silently retains its default value. + */ + explicit CDStarRepeaterConfig(const std::string& filePath); ~CDStarRepeaterConfig(); - void getCallsign(wxString& callsign, wxString& gateway, DSTAR_MODE& mode, ACK_TYPE& ack, bool& restriction, bool& rpt1Validation, bool& dtmfBlanking, bool& errorReply) const; - void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); + // [General] + void getCallsign(std::string& callsign, std::string& gateway, DSTAR_MODE& mode, ACK_TYPE& ack, bool& restriction, bool& rpt1Validation, bool& dtmfBlanking, bool& errorReply) const; - void getNetwork(wxString& gatewayAddress, unsigned int& gatewayPort, wxString& localAddress, unsigned int& localPort, wxString& name) const; - void setNetwork(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name); + // [Log] + void getLog(std::string& filePath, unsigned int& fileLevel, unsigned int& displayLevel, unsigned int& mqttLevel) const; - void getModem(wxString& type) const; - void setModem(const wxString& type); + // [Paths] + void getPaths(std::string& dataDir, std::string& audioDir) const; - void getTimes(unsigned int& timeout, unsigned int& ackTime) const; - void setTimes(unsigned int timeout, unsigned int ackTime); + // [Network] + void getNetwork(std::string& gatewayAddress, unsigned int& gatewayPort, std::string& localAddress, unsigned int& localPort, std::string& name) const; + + // [Modem] + void getModem(std::string& type) const; - void getBeacon(unsigned int& time, wxString& text, bool& voice, TEXT_LANG& language) const; - void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language); + // [Times] + void getTimes(unsigned int& timeout, unsigned int& ackTime) const; - void getAnnouncement(bool& enabled, unsigned int& time, wxString& recordRPT1, wxString& recordRPT2, wxString& deleteRPT1, wxString& deleteRPT2) const; - void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); + // [Beacon] + void getBeacon(unsigned int& time, std::string& text, bool& voice, TEXT_LANG& language) const; - void getControl(bool& enabled, wxString& rpt1Callsign, wxString& rpt2Callsign, wxString& shutdown, wxString& startup, wxString& status1, wxString& status2, wxString& status3, wxString& status4, wxString& status5, wxString& command1, wxString& command1Line, wxString& command2, wxString& command2Line, wxString& command5, wxString& command5Line, wxString& command6, wxString& command6Line, wxString& command3, wxString& command3Line, wxString& command4, wxString& command4Line, wxString& output1, wxString& output2, wxString& output3, wxString& output4) const; + // [Announcement] + void getAnnouncement(bool& enabled, unsigned int& time, std::string& recordRPT1, std::string& recordRPT2, std::string& deleteRPT1, std::string& deleteRPT2) const; - void setControl(bool enabled, const wxString& rpt1Callsign, const wxString& rpt2Callsign, const wxString& shutdown, const wxString& startup, const wxString& status1, const wxString& status2, const wxString& status3, const wxString& status4, const wxString& status5, const wxString& command1, const wxString& command1Line, const wxString& command2, const wxString& command2Line, const wxString& command3, const wxString& command3Line, const wxString& command4, const wxString& command4Line, const wxString& command5, const wxString& command5Line, const wxString& command6, const wxString& command6Line, const wxString& output1, const wxString& output2, const wxString& output3, const wxString& output4); + // [Control] + void getControl(bool& enabled, std::string& rpt1Callsign, std::string& rpt2Callsign, std::string& shutdown, std::string& startup, std::string& status1, std::string& status2, std::string& status3, std::string& status4, std::string& status5, std::string& command1, std::string& command1Line, std::string& command2, std::string& command2Line, std::string& command3, std::string& command3Line, std::string& command4, std::string& command4Line, std::string& command5, std::string& command5Line, std::string& command6, std::string& command6Line, std::string& output1, std::string& output2, std::string& output3, std::string& output4) const; - void getController(wxString& type, unsigned int& serialConfig, bool& pttInvert, unsigned int& activeHangTime) const; - void setController(const wxString& type, unsigned int serialConfig, bool pttInvert, unsigned int activeHangTime); + // [Controller] + void getController(std::string& type, unsigned int& serialConfig, bool& pttInvert, unsigned int& activeHangTime) const; + // [Outputs] void getOutputs(bool& out1, bool& out2, bool& out3, bool& out4) const; - void setOutputs(bool out1, bool out2, bool out3, bool out4); + // [Frame Logging] void getLogging(bool& logging) const; - void setLogging(bool logging); - void getPosition(int& x, int& y) const; - void setPosition(int x, int y); + // [Whitelist] / [Blacklist] / [Greylist] (empty string = not configured) + void getWhitelist(std::string& file) const; + void getBlacklist(std::string& file) const; + void getGreylist(std::string& file) const; - void getDVAP(wxString& port, unsigned int& frequency, int& power, int& squelch) const; - void setDVAP(const wxString& port, unsigned int frequency, int power, int squelch); + // [DVAP] + void getDVAP(std::string& port, unsigned int& frequency, int& power, int& squelch) const; + // [GMSK] void getGMSK(USB_INTERFACE& type, unsigned int& address) const; - void setGMSK(USB_INTERFACE type, unsigned int address); - void getDVRPTR1(wxString& port, bool& rxInvert, bool& txInvert, bool& channel, unsigned int& modLevel, unsigned int& txDelay) const; - void setDVRPTR1(const wxString& port, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay); + // [DV-RPTR V1] + void getDVRPTR1(std::string& port, bool& rxInvert, bool& txInvert, bool& channel, unsigned int& modLevel, unsigned int& txDelay) const; - void getDVRPTR2(CONNECTION_TYPE& connectionType, wxString& usbPort, wxString& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const; - void setDVRPTR2(CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay); + // [DV-RPTR V2] + void getDVRPTR2(CONNECTION_TYPE& connectionType, std::string& usbPort, std::string& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const; - void getDVRPTR3(CONNECTION_TYPE& connectionType, wxString& usbPort, wxString& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const; - void setDVRPTR3(CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay); + // [DV-RPTR V3] + void getDVRPTR3(CONNECTION_TYPE& connectionType, std::string& usbPort, std::string& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const; - void getDVMEGA(wxString& port, DVMEGA_VARIANT& variant, bool& rxInvert, bool& txInvert, unsigned int& txDelay, unsigned int& rxFrequency, unsigned int& txFrequency, unsigned int& power) const; - void setDVMEGA(const wxString& port, DVMEGA_VARIANT variant, bool rxInvert, bool txInvert, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power); + // [DVMEGA] + void getDVMEGA(std::string& port, DVMEGA_VARIANT& variant, bool& rxInvert, bool& txInvert, unsigned int& txDelay, unsigned int& rxFrequency, unsigned int& txFrequency, unsigned int& power) const; - void getMMDVM(wxString& port, bool& rxInvert, bool& txInvert, bool& pttInvert, unsigned int& txDelay, unsigned int& rxLevel, unsigned int& txLevel) const; - void setMMDVM(const wxString& port, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel); + // [MMDVM] + void getMMDVM(std::string& port, bool& rxInvert, bool& txInvert, bool& pttInvert, unsigned int& txDelay, unsigned int& rxLevel, unsigned int& txLevel) const; - void getSoundCard(wxString& rxDevice, wxString& txDevice, bool& rxInvert, bool& txInvert, wxFloat32& rxLevel, wxFloat32& txLevel, unsigned int& txDelay, unsigned int& txTail) const; - void setSoundCard(const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail); + // [Sound Card] + void getSoundCard(std::string& rxDevice, std::string& txDevice, bool& rxInvert, bool& txInvert, float& rxLevel, float& txLevel, unsigned int& txDelay, unsigned int& txTail) const; - void getSplit(wxString& localAddress, unsigned int& localPort, wxArrayString& transmitterNames, wxArrayString& receiverNames, unsigned int& timeout) const; - void setSplit(const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout); + // [Split] + void getSplit(std::string& localAddress, unsigned int& localPort, std::vector& transmitterNames, std::vector& receiverNames, unsigned int& timeout) const; - void getIcom(wxString& port) const; - void setIcom(const wxString& port); + // [Icom] + void getIcom(std::string& port) const; #if defined(MQTT) - void getMQTT(wxString& host, unsigned int& port, bool& auth, wxString& username, wxString& password, unsigned int& keepalive, wxString& name) const; - void setMQTT(const wxString& host, unsigned int port, bool auth, const wxString& username, const wxString& password, unsigned int keepalive, const wxString& name); + // [MQTT] + void getMQTT(std::string& host, unsigned int& port, bool& auth, std::string& username, std::string& password, unsigned int& keepalive, std::string& name) const; #endif - bool write(); - private: -#if defined(__WINDOWS__) - wxConfigBase* m_config; - wxString m_name; -#endif - wxFileName m_fileName; - wxString m_callsign; - wxString m_gateway; + // [General] + std::string m_callsign; + std::string m_gateway; DSTAR_MODE m_mode; ACK_TYPE m_ack; bool m_restriction; bool m_rpt1Validation; bool m_dtmfBlanking; bool m_errorReply; - wxString m_gatewayAddress; + + // [Log] + std::string m_logFilePath; + unsigned int m_logFileLevel; + unsigned int m_logDisplayLevel; + unsigned int m_logMQTTLevel; + + // [Paths] + std::string m_dataDir; + std::string m_audioDir; + + // [Network] + std::string m_gatewayAddress; unsigned int m_gatewayPort; - wxString m_localAddress; + std::string m_localAddress; unsigned int m_localPort; - wxString m_networkName; - wxString m_modemType; + std::string m_networkName; + + // [Modem] + std::string m_modemType; + + // [Times] unsigned int m_timeout; unsigned int m_ackTime; + + // [Beacon] unsigned int m_beaconTime; - wxString m_beaconText; + std::string m_beaconText; bool m_beaconVoice; TEXT_LANG m_language; + + // [Announcement] bool m_announcementEnabled; unsigned int m_announcementTime; - wxString m_announcementRecordRPT1; - wxString m_announcementRecordRPT2; - wxString m_announcementDeleteRPT1; - wxString m_announcementDeleteRPT2; + std::string m_announcementRecordRPT1; + std::string m_announcementRecordRPT2; + std::string m_announcementDeleteRPT1; + std::string m_announcementDeleteRPT2; + + // [Control] bool m_controlEnabled; - wxString m_controlRpt1Callsign; - wxString m_controlRpt2Callsign; - wxString m_controlShutdown; - wxString m_controlStartup; - wxString m_controlStatus1; - wxString m_controlStatus2; - wxString m_controlStatus3; - wxString m_controlStatus4; - wxString m_controlStatus5; - wxString m_controlCommand1; - wxString m_controlCommand1Line; - wxString m_controlCommand2; - wxString m_controlCommand2Line; - wxString m_controlCommand3; - wxString m_controlCommand3Line; - wxString m_controlCommand4; - wxString m_controlCommand4Line; - wxString m_controlCommand5; - wxString m_controlCommand5Line; - wxString m_controlCommand6; - wxString m_controlCommand6Line; - wxString m_controlOutput1; - wxString m_controlOutput2; - wxString m_controlOutput3; - wxString m_controlOutput4; - wxString m_controllerType; + std::string m_controlRpt1Callsign; + std::string m_controlRpt2Callsign; + std::string m_controlShutdown; + std::string m_controlStartup; + std::string m_controlStatus1; + std::string m_controlStatus2; + std::string m_controlStatus3; + std::string m_controlStatus4; + std::string m_controlStatus5; + std::string m_controlCommand1; + std::string m_controlCommand1Line; + std::string m_controlCommand2; + std::string m_controlCommand2Line; + std::string m_controlCommand3; + std::string m_controlCommand3Line; + std::string m_controlCommand4; + std::string m_controlCommand4Line; + std::string m_controlCommand5; + std::string m_controlCommand5Line; + std::string m_controlCommand6; + std::string m_controlCommand6Line; + std::string m_controlOutput1; + std::string m_controlOutput2; + std::string m_controlOutput3; + std::string m_controlOutput4; + + // [Controller] + std::string m_controllerType; unsigned int m_serialConfig; bool m_pttInvert; unsigned int m_activeHangTime; + + // [Outputs] bool m_output1; bool m_output2; bool m_output3; bool m_output4; + + // [Frame Logging] bool m_logging; - int m_x; - int m_y; - // DVAP - wxString m_dvapPort; + // [Whitelist] / [Blacklist] / [Greylist] (empty = not configured) + std::string m_whitelistFile; + std::string m_blacklistFile; + std::string m_greylistFile; + + // [DVAP] + std::string m_dvapPort; unsigned int m_dvapFrequency; int m_dvapPower; int m_dvapSquelch; - // GMSK + // [GMSK] USB_INTERFACE m_gmskInterface; unsigned int m_gmskAddress; - // DV-RPTR 1 - wxString m_dvrptr1Port; + // [DV-RPTR V1] + std::string m_dvrptr1Port; bool m_dvrptr1RXInvert; bool m_dvrptr1TXInvert; bool m_dvrptr1Channel; unsigned int m_dvrptr1ModLevel; unsigned int m_dvrptr1TXDelay; - // DV-RPTR 2 + // [DV-RPTR V2] CONNECTION_TYPE m_dvrptr2Connection; - wxString m_dvrptr2USBPort; - wxString m_dvrptr2Address; + std::string m_dvrptr2USBPort; + std::string m_dvrptr2Address; unsigned int m_dvrptr2Port; bool m_dvrptr2TXInvert; unsigned int m_dvrptr2ModLevel; unsigned int m_dvrptr2TXDelay; - // DV-RPTR 3 + // [DV-RPTR V3] CONNECTION_TYPE m_dvrptr3Connection; - wxString m_dvrptr3USBPort; - wxString m_dvrptr3Address; + std::string m_dvrptr3USBPort; + std::string m_dvrptr3Address; unsigned int m_dvrptr3Port; bool m_dvrptr3TXInvert; unsigned int m_dvrptr3ModLevel; unsigned int m_dvrptr3TXDelay; - // DVMEGA - wxString m_dvmegaPort; + // [DVMEGA] + std::string m_dvmegaPort; DVMEGA_VARIANT m_dvmegaVariant; bool m_dvmegaRXInvert; bool m_dvmegaTXInvert; @@ -221,8 +271,8 @@ private: unsigned int m_dvmegaTXFrequency; unsigned int m_dvmegaPower; - // MMDVM - wxString m_mmdvmPort; + // [MMDVM] + std::string m_mmdvmPort; bool m_mmdvmRXInvert; bool m_mmdvmTXInvert; bool m_mmdvmPTTInvert; @@ -230,35 +280,35 @@ private: unsigned int m_mmdvmRXLevel; unsigned int m_mmdvmTXLevel; - // Sound Card - wxString m_soundCardRXDevice; - wxString m_soundCardTXDevice; + // [Sound Card] + std::string m_soundCardRXDevice; + std::string m_soundCardTXDevice; bool m_soundCardRXInvert; bool m_soundCardTXInvert; - wxFloat32 m_soundCardRXLevel; - wxFloat32 m_soundCardTXLevel; + float m_soundCardRXLevel; + float m_soundCardTXLevel; unsigned int m_soundCardTXDelay; unsigned int m_soundCardTXTail; - // Split - wxString m_splitLocalAddress; - unsigned int m_splitLocalPort; - wxArrayString m_splitTXNames; - wxArrayString m_splitRXNames; - unsigned int m_splitTimeout; + // [Split] + std::string m_splitLocalAddress; + unsigned int m_splitLocalPort; + std::vector m_splitTXNames; + std::vector m_splitRXNames; + unsigned int m_splitTimeout; - // Icom Access Point/Terminal Mode - wxString m_icomPort; + // [Icom] + std::string m_icomPort; #if defined(MQTT) - // MQTT - wxString m_mqttHost; + // [MQTT] + std::string m_mqttHost; unsigned int m_mqttPort; bool m_mqttAuth; - wxString m_mqttUsername; - wxString m_mqttPassword; + std::string m_mqttUsername; + std::string m_mqttPassword; unsigned int m_mqttKeepalive; - wxString m_mqttName; + std::string m_mqttName; #endif }; diff --git a/Common/DStarScrambler.cpp b/Common/DStarScrambler.cpp index ad1c735..c6510d8 100644 --- a/Common/DStarScrambler.cpp +++ b/Common/DStarScrambler.cpp @@ -13,7 +13,7 @@ #include "DStarScrambler.h" -#include +#include static const bool SCRAMBLER_TABLE_BITS[] = { false, false, false, false, true, true, true, false, true, true, true, true, false, false, true, false, @@ -22,9 +22,9 @@ static const bool SCRAMBLER_TABLE_BITS[] = { true, false, true, true, false, true, true, false, false, false, false, false, true, true, false, false, true, true, false, true, false, true, false, false, true, true, true, false, false, true, true, true, true, false, true, true, false, true, false, false, false, false, true, false, true, false, true, false, - true, true, true, true, true, false, true, false, false, true, false, true, false, false, false, true, + true, true, true, true, true, false, true, false, false, true, false, true, false, false, false, true, true, false, true, true, true, false, false, false, true, true, true, true, true, true, true, false, - false, false, false, true, true, true, false, true, true, true, true, false, false, true, false, true, + false, false, false, true, true, true, false, true, true, true, true, false, false, true, false, true, true, false, false, true, false, false, true, false, false, false, false, false, false, true, false, false, false, true, false, false, true, true, false, false, false, true, false, true, true, true, false, true, false, true, true, false, true, true, false, false, false, false, false, true, true, false, false, true, @@ -85,7 +85,7 @@ CDStarScrambler::~CDStarScrambler() void CDStarScrambler::process(bool* inOut, unsigned int length) { - wxASSERT(inOut != 0); + assert(inOut != nullptr); for (unsigned int i = 0U; i < length; i++) { inOut[i] ^= SCRAMBLER_TABLE_BITS[m_count++]; @@ -97,8 +97,8 @@ void CDStarScrambler::process(bool* inOut, unsigned int length) void CDStarScrambler::process(const bool* in, bool* out, unsigned int length) { - wxASSERT(in != 0); - wxASSERT(out != 0); + assert(in != nullptr); + assert(out != nullptr); for (unsigned int i = 0U; i < length; i++) { out[i] = in[i] ^ SCRAMBLER_TABLE_BITS[m_count++]; @@ -110,7 +110,7 @@ void CDStarScrambler::process(const bool* in, bool* out, unsigned int length) void CDStarScrambler::process(unsigned char* inOut, unsigned int length) { - wxASSERT(inOut != 0); + assert(inOut != nullptr); for (unsigned int i = 0U; i < length; i++) { inOut[i] ^= SCRAMBLER_TABLE_BYTES[m_count++]; @@ -122,8 +122,8 @@ void CDStarScrambler::process(unsigned char* inOut, unsigned int length) void CDStarScrambler::process(const unsigned char* in, unsigned char* out, unsigned int length) { - wxASSERT(in != 0); - wxASSERT(out != 0); + assert(in != nullptr); + assert(out != nullptr); for (unsigned int i = 0U; i < length; i++) { out[i] = in[i] ^ SCRAMBLER_TABLE_BYTES[m_count++]; diff --git a/Common/DStarScrambler.h b/Common/DStarScrambler.h index 40db775..f44f716 100644 --- a/Common/DStarScrambler.h +++ b/Common/DStarScrambler.h @@ -14,6 +14,18 @@ #ifndef DStarScrambler_H #define DStarScrambler_H +/* + * D-Star bit scrambler applied to the physical RF layer. + * + * The D-Star standard mandates scrambling of the bit stream before GMSK + * modulation to randomise the data and avoid long runs of identical bits that + * would stress the PLL in receivers. The scrambler uses a linear feedback + * shift register with the polynomial x^17 + x^12 + 1. + * + * process() operates in-place (inOut form) or with separate input/output + * buffers, and handles both bit-per-bool and packed-byte representations. + * reset() resets the LFSR to its initial state. + */ class CDStarScrambler { public: CDStarScrambler(); @@ -28,7 +40,7 @@ public: void reset(); private: - unsigned int m_count; + unsigned int m_count; // Current position in the LFSR sequence. }; #endif diff --git a/Common/DVAPController.cpp b/Common/DVAPController.cpp index e8a0fee..bf3be85 100644 --- a/Common/DVAPController.cpp +++ b/Common/DVAPController.cpp @@ -19,8 +19,15 @@ #include "CCITTChecksumReverse.h" #include "DVAPController.h" #include "DStarDefines.h" +#include "Logger.h" #include "Timer.h" +#include +#include +#include +#include +#include "EndianCompat.h" + const unsigned char DVAP_REQ_NAME[] = {0x04, 0x20, 0x01, 0x00}; const unsigned int DVAP_REQ_NAME_LEN = 4U; @@ -125,7 +132,7 @@ const unsigned int BUFFER_LENGTH = 200U; const unsigned int DVAP_DUMP_LENGTH = 30U; -CDVAPController::CDVAPController(const wxString& port, unsigned int frequency, int power, int squelch) : +CDVAPController::CDVAPController(const std::string& port, unsigned int frequency, int power, int squelch) : CModem(), m_serial(port, SERIAL_230400), m_frequency(frequency), @@ -133,24 +140,24 @@ m_power(power), m_squelch(squelch), m_squelchOpen(false), m_signal(0), -m_buffer(NULL), +m_buffer(nullptr), m_streamId(0U), m_framePos(0U), m_seq(0U), m_txData(1000U) #if defined(DVAP_DUMP) , -m_dvapData(NULL), -m_dvapLength(NULL), +m_dvapData(nullptr), +m_dvapLength(nullptr), m_dvapIndex(0U) #endif { - wxASSERT(!port.IsEmpty()); - wxASSERT((frequency >= 144000000U && frequency <= 148000000U) || - (frequency >= 220000000U && frequency <= 225000000U) || - (frequency >= 420000000U && frequency <= 450000000U)); - wxASSERT(power >= -12 && power <= 10); - wxASSERT(squelch >= -128 && squelch <= -45); + assert(!port.empty()); + assert((frequency >= 144000000U && frequency <= 148000000U) || + (frequency >= 220000000U && frequency <= 225000000U) || + (frequency >= 420000000U && frequency <= 450000000U)); + assert(power >= -12 && power <= 10); + assert(squelch >= -128 && squelch <= -45); m_buffer = new unsigned char[BUFFER_LENGTH]; @@ -238,16 +245,19 @@ bool CDVAPController::start() return false; } - Create(); - SetPriority(100U); - Run(); + m_thread = std::thread(&CDVAPController::entry, this); return true; } -void* CDVAPController::Entry() +// Main controller thread. Runs until m_stopped is set by CModem::stop(). +// The loop is driven by RT_STATE packets (sent every ~20ms by the hardware) +// which carry the TX buffer space count. Data is written to the wire only +// when space > 0 and an RT_STATE has just been received, so writes are +// naturally rate-limited to the hardware's 20ms frame cadence. +void CDVAPController::entry() { - wxLogMessage(wxT("Starting DVAP Controller thread")); + wxLogMessage("Starting DVAP Controller thread"); // Clock every 5ms-ish CTimer pollTimer(200U, 2U); @@ -272,31 +282,32 @@ void* CDVAPController::Entry() case RT_TIMEOUT: break; case RT_ERROR: - wxLogMessage(wxT("Stopping DVAP Controller thread")); + wxLogMessage("Stopping DVAP Controller thread"); #if defined(DVAP_DUMP) dumpPackets(); #endif m_serial.close(); - return NULL; + delete[] writeBuffer; + return; case RT_STATE: m_signal = int(m_buffer[4U]) - 256; m_squelchOpen = m_buffer[5U] == 0x01U; space = m_buffer[6U]; break; case RT_PTT: - m_tx = m_buffer[4U] == 0x01U; + m_tx = m_buffer[4U] == 0x01U; break; case RT_START: break; case RT_STOP: - wxLogWarning(wxT("DVAP has stopped, restarting")); + wxLogWarning("DVAP has stopped, restarting"); #if defined(DVAP_DUMP) dumpPackets(); #endif startDVAP(); break; case RT_HEADER: { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char hdr[2U]; hdr[0U] = DSMTT_HEADER; @@ -309,7 +320,7 @@ void* CDVAPController::Entry() case RT_HEADER_ACK: break; case RT_GMSK_DATA: { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); bool end = (m_buffer[4U] & 0x40U) == 0x40U; if (end) { @@ -328,7 +339,7 @@ void* CDVAPController::Entry() } break; case RT_FM_DATA: - wxLogWarning(wxT("The DVAP has gone into FM mode, restarting the DVAP")); + wxLogWarning("The DVAP has gone into FM mode, restarting the DVAP"); #if defined(DVAP_DUMP) dumpPackets(); #endif @@ -337,8 +348,8 @@ void* CDVAPController::Entry() startDVAP(); break; default: - wxLogMessage(wxT("Unknown message")); - CUtils::dump(wxT("Buffer dump"), m_buffer, length); + wxLogMessage("Unknown message"); + CUtils::dump("Buffer dump", m_buffer, length); #if defined(DVAP_DUMP) dumpPackets(); #endif @@ -348,7 +359,7 @@ void* CDVAPController::Entry() // Use the status packet every 20ms to trigger the sending of data to the DVAP if (space > 0U && type == RT_STATE) { if (writeLength == 0U && m_txData.hasData()) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); m_txData.getData(&writeLength, 1U); m_txData.getData(writeBuffer, writeLength); @@ -356,55 +367,53 @@ void* CDVAPController::Entry() // Only send the header when the TX is off if (!m_tx && writeLength == DVAP_HEADER_LEN) { - // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + // CUtils::dump("Write Header", writeBuffer, writeLength); #if defined(DVAP_DUMP) storePacket(writeBuffer, writeLength); #endif int ret = m_serial.write(writeBuffer, writeLength); if (ret != int(writeLength)) - wxLogWarning(wxT("Error when writing the header to the DVAP")); + wxLogWarning("Error when writing the header to the DVAP"); writeLength = 0U; space--; } - + if (writeLength == DVAP_GMSK_DATA_LEN) { - // CUtils::dump(wxT("Write Data"), writeBuffer, writeLength); + // CUtils::dump("Write Data", writeBuffer, writeLength); #if defined(DVAP_DUMP) storePacket(writeBuffer, writeLength); #endif int ret = m_serial.write(writeBuffer, writeLength); if (ret != int(writeLength)) - wxLogWarning(wxT("Error when writing data to the DVAP")); + wxLogWarning("Error when writing data to the DVAP"); writeLength = 0U; space--; } } - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); pollTimer.clock(); } - wxLogMessage(wxT("Stopping DVAP Controller thread")); + wxLogMessage("Stopping DVAP Controller thread"); stopDVAP(); delete[] writeBuffer; m_serial.close(); - - return NULL; } bool CDVAPController::writeHeader(const CHeaderData& header) { bool ret = m_txData.hasSpace(DVAP_HEADER_LEN + 1U); if (!ret) { - wxLogWarning(wxT("No space to write the header")); + wxLogWarning("No space to write the header"); return false; } @@ -414,8 +423,8 @@ bool CDVAPController::writeHeader(const CHeaderData& header) ::memcpy(buffer, DVAP_HEADER, DVAP_HEADER_LEN); - wxUint16 sid = wxUINT16_SWAP_ON_BE(m_streamId); - ::memcpy(buffer + 2U, &sid, sizeof(wxUint16)); + uint16_t sid = htole16(m_streamId); + ::memcpy(buffer + 2U, &sid, sizeof(uint16_t)); buffer[4U] = 0x80U; buffer[5U] = 0U; @@ -426,25 +435,25 @@ bool CDVAPController::writeHeader(const CHeaderData& header) buffer[7U] = header.getFlag2(); buffer[8U] = header.getFlag3(); - wxString rpt2 = header.getRptCall2(); - for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 9U] = rpt2.GetChar(i); + std::string rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 9U] = rpt2[i]; - wxString rpt1 = header.getRptCall1(); - for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 17U] = rpt1.GetChar(i); + std::string rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 17U] = rpt1[i]; - wxString your = header.getYourCall(); - for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 25U] = your.GetChar(i); + std::string your = header.getYourCall(); + for (unsigned int i = 0U; i < your.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 25U] = your[i]; - wxString my1 = header.getMyCall1(); - for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 33U] = my1.GetChar(i); + std::string my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 33U] = my1[i]; - wxString my2 = header.getMyCall2(); - for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) - buffer[i + 41U] = my2.GetChar(i); + std::string my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.size() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 41U] = my2[i]; CCCITTChecksumReverse cksum; cksum.update(buffer + 6U, RADIO_HEADER_LENGTH_BYTES - 2U); @@ -453,7 +462,7 @@ bool CDVAPController::writeHeader(const CHeaderData& header) m_framePos = 0U; m_seq = 0U; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char len = DVAP_HEADER_LEN; m_txData.addData(&len, 1U); @@ -467,7 +476,7 @@ bool CDVAPController::writeData(const unsigned char* data, unsigned int, bool en { bool ret = m_txData.hasSpace(DVAP_GMSK_DATA_LEN + 1U); if (!ret) { - wxLogWarning(wxT("No space to write data")); + wxLogWarning("No space to write data"); return false; } @@ -478,8 +487,8 @@ bool CDVAPController::writeData(const unsigned char* data, unsigned int, bool en if (::memcmp(data + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES) == 0) m_framePos = 0U; - wxUint16 sid = wxUINT16_SWAP_ON_BE(m_streamId); - ::memcpy(buffer + 2U, &sid, sizeof(wxUint16)); + uint16_t sid = htole16(m_streamId); + ::memcpy(buffer + 2U, &sid, sizeof(uint16_t)); buffer[4U] = m_framePos; buffer[5U] = m_seq; @@ -489,7 +498,7 @@ bool CDVAPController::writeData(const unsigned char* data, unsigned int, bool en ::memcpy(buffer + 6U, data, DV_FRAME_LENGTH_BYTES); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char len = DVAP_GMSK_DATA_LEN; m_txData.addData(&len, 1U); @@ -545,14 +554,14 @@ bool CDVAPController::getName() return false; } - ::wxMilliSleep(50UL); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); resp = getResponse(m_buffer, length); if (resp != RT_NAME) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The Dongle is not replying with its name")); + wxLogError("The Dongle is not replying with its name"); return false; } } @@ -560,7 +569,7 @@ bool CDVAPController::getName() bool cmp = ::memcmp(m_buffer, DVAP_RESP_NAME, length) == 0; if (!cmp) { - wxLogError(wxT("The Dongle is not responding as a DVAP")); + wxLogError("The Dongle is not responding as a DVAP"); return false; } @@ -579,21 +588,21 @@ bool CDVAPController::getFirmware() return false; } - ::wxMilliSleep(50UL); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); resp = getResponse(m_buffer, length); if (resp != RT_FIRMWARE) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVAP is not responding with its firmware version")); + wxLogError("The DVAP is not responding with its firmware version"); return false; } } } while (resp != RT_FIRMWARE); unsigned int version = m_buffer[6U] * 256U + m_buffer[5U]; - wxLogInfo(wxT("DVAP Firmware version: %u.%u"), version / 100U, version % 100U); + wxLogInfo("DVAP Firmware version: %u.%u", version / 100U, version % 100U); return true; } @@ -610,21 +619,21 @@ bool CDVAPController::getSerial() return false; } - ::wxMilliSleep(50UL); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); resp = getResponse(m_buffer, length); if (resp != RT_SERIAL) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVAP is not responding with its serial number")); + wxLogError("The DVAP is not responding with its serial number"); return false; } } } while (resp != RT_SERIAL); - wxString serial((char*)(m_buffer + 4U), wxConvLocal, length - 5U); - wxLogInfo(wxT("DVAP Serial number: %s"), serial.c_str()); + std::string serial((char*)(m_buffer + 4U), length - 5U); + wxLogInfo("DVAP Serial number: %s", serial.c_str()); return true; } @@ -641,14 +650,14 @@ bool CDVAPController::startDVAP() return false; } - ::wxMilliSleep(50UL); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); resp = getResponse(m_buffer, length); if (resp != RT_START) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVAP is not responding to the start command")); + wxLogError("The DVAP is not responding to the start command"); return false; } } @@ -669,14 +678,14 @@ bool CDVAPController::stopDVAP() return false; } - ::wxMilliSleep(50UL); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); resp = getResponse(m_buffer, length); if (resp != RT_STOP) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVAP is not responding to the stop command")); + wxLogError("The DVAP is not responding to the stop command"); return false; } } @@ -697,14 +706,14 @@ bool CDVAPController::setModulation() return false; } - ::wxMilliSleep(50UL); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); resp = getResponse(m_buffer, length); if (resp != RT_MODULATION) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVAP is not responding to the modulation command")); + wxLogError("The DVAP is not responding to the modulation command"); return false; } } @@ -725,14 +734,14 @@ bool CDVAPController::setMode() return false; } - ::wxMilliSleep(50UL); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); resp = getResponse(m_buffer, length); if (resp != RT_MODE) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVAP is not responding to the mode command")); + wxLogError("The DVAP is not responding to the mode command"); return false; } } @@ -749,7 +758,7 @@ bool CDVAPController::setSquelch() do { unsigned char buffer[10U]; ::memcpy(buffer, DVAP_REQ_SQUELCH, DVAP_REQ_SQUELCH_LEN); - ::memcpy(buffer + 4U, &m_squelch, sizeof(wxInt8)); + ::memcpy(buffer + 4U, &m_squelch, sizeof(int8_t)); int ret = m_serial.write(buffer, DVAP_REQ_SQUELCH_LEN); if (ret != int(DVAP_REQ_SQUELCH_LEN)) { @@ -757,14 +766,14 @@ bool CDVAPController::setSquelch() return false; } - ::wxMilliSleep(50UL); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); resp = getResponse(m_buffer, length); if (resp != RT_SQUELCH) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVAP is not responding to the squelch command")); + wxLogError("The DVAP is not responding to the squelch command"); return false; } } @@ -782,8 +791,8 @@ bool CDVAPController::setPower() unsigned char buffer[10U]; ::memcpy(buffer, DVAP_REQ_POWER, DVAP_REQ_POWER_LEN); - wxInt16 power = wxINT16_SWAP_ON_BE(m_power); - ::memcpy(buffer + 4U, &power, sizeof(wxInt16)); + int16_t power = htole16(m_power); + ::memcpy(buffer + 4U, &power, sizeof(int16_t)); int ret = m_serial.write(buffer, DVAP_REQ_POWER_LEN); if (ret != int(DVAP_REQ_POWER_LEN)) { @@ -791,14 +800,14 @@ bool CDVAPController::setPower() return false; } - ::wxMilliSleep(50UL); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); resp = getResponse(m_buffer, length); if (resp != RT_POWER) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVAP is not responding to the power command")); + wxLogError("The DVAP is not responding to the power command"); return false; } } @@ -819,29 +828,29 @@ bool CDVAPController::setFrequency() return false; } - ::wxMilliSleep(50UL); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); resp = getResponse(m_buffer, length); if (resp != RT_FREQLIMITS) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVAP is not responding to the frequency limits command")); + wxLogError("The DVAP is not responding to the frequency limits command"); return false; } } } while (resp != RT_FREQLIMITS); - wxUint32* pFreq1 = (wxUint32*)(m_buffer + 4U); - wxUint32* pFreq2 = (wxUint32*)(m_buffer + 8U); + uint32_t* pFreq1 = (uint32_t*)(m_buffer + 4U); + uint32_t* pFreq2 = (uint32_t*)(m_buffer + 8U); - wxUint32 lower = wxUINT32_SWAP_ON_BE(*pFreq1); - wxUint32 upper = wxUINT32_SWAP_ON_BE(*pFreq2); + uint32_t lower = le32toh(*pFreq1); + uint32_t upper = le32toh(*pFreq2); - wxLogInfo(wxT("DVAP frequency limits are %u Hz to %u Hz"), lower, upper); + wxLogInfo("DVAP frequency limits are %u Hz to %u Hz", lower, upper); if (m_frequency < lower || m_frequency > upper) { - wxLogError(wxT("The required frequency is out of the range of the DVAP hardware")); + wxLogError("The required frequency is out of the range of the DVAP hardware"); m_serial.close(); return false; } @@ -851,8 +860,8 @@ bool CDVAPController::setFrequency() unsigned char buffer[10U]; ::memcpy(buffer, DVAP_REQ_FREQUENCY, DVAP_REQ_FREQUENCY_LEN); - wxUint32 frequency = wxUINT32_SWAP_ON_BE(m_frequency); - ::memcpy(buffer + 4U, &frequency, sizeof(wxUint32)); + uint32_t frequency = htole32(m_frequency); + ::memcpy(buffer + 4U, &frequency, sizeof(uint32_t)); int ret = m_serial.write(buffer, DVAP_REQ_FREQUENCY_LEN); if (ret != int(DVAP_REQ_FREQUENCY_LEN)) { @@ -860,14 +869,14 @@ bool CDVAPController::setFrequency() return false; } - ::wxMilliSleep(50UL); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); resp = getResponse(m_buffer, length); if (resp != RT_FREQUENCY) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVAP is not responding to the frequency command")); + wxLogError("The DVAP is not responding to the frequency command"); return false; } } @@ -876,6 +885,16 @@ bool CDVAPController::setFrequency() return true; } +// Reads one complete DVAP binary frame. +// +// Wire format: [len_lo] [flags|len_hi] [cmd_lo] [cmd_hi] [payload...] +// Frame length = byte[0] + (byte[1] & 0x1F) * 256. +// Maximum sane length is capped at 50 bytes; anything larger indicates +// framing loss and triggers resync(). +// +// The status packet (0x07 0x20 0x90 0x00 ...) occasionally arrives with +// its first byte missing from the UART FIFO; the workaround below detects +// the truncated signature {0x20, 0x90} and prepends the missing 0x07. RESP_TYPE CDVAPController::getResponse(unsigned char *buffer, unsigned int& length) { int ret = m_serial.read(buffer, DVAP_HEADER_LENGTH); @@ -898,7 +917,7 @@ RESP_TYPE CDVAPController::getResponse(unsigned char *buffer, unsigned int& leng // Check for silliness if (length > 50U) { - CUtils::dump(wxT("Bad DVAP header"), buffer, DVAP_HEADER_LENGTH); + CUtils::dump("Bad DVAP header", buffer, DVAP_HEADER_LENGTH); #if defined(DVAP_DUMP) dumpPackets(); #endif @@ -912,11 +931,14 @@ RESP_TYPE CDVAPController::getResponse(unsigned char *buffer, unsigned int& leng return RT_ERROR; if (ret > 0) offset += ret; - if (ret == 0) - Sleep(5UL); + if (ret == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + if (m_stopped) + return RT_TIMEOUT; + } } - // CUtils::dump(wxT("Received"), buffer, length); + // CUtils::dump("Received", buffer, length); if (::memcmp(buffer, DVAP_STATUS, 4U) == 0) return RT_STATE; @@ -955,7 +977,7 @@ RESP_TYPE CDVAPController::getResponse(unsigned char *buffer, unsigned int& leng else if (::memcmp(buffer, DVAP_RESP_SQUELCH, 4U) == 0) return RT_SQUELCH; else { - CUtils::dump(wxT("Bad DVAP data"), buffer, length); + CUtils::dump("Bad DVAP data", buffer, length); #if defined(DVAP_DUMP) dumpPackets(); #endif @@ -964,30 +986,41 @@ RESP_TYPE CDVAPController::getResponse(unsigned char *buffer, unsigned int& leng } } +// Discards bytes from the serial port one at a time, shifting them through a +// sliding window, until the 7-byte status packet signature is matched at +// bytes[0..3]. This re-aligns the byte boundary after a framing error. void CDVAPController::resync() { - wxLogWarning(wxT("Resynchronising the DVAP data stream")); + wxLogWarning("Resynchronising the DVAP data stream"); unsigned char data[DVAP_STATUS_LEN]; ::memset(data, 0x00U, DVAP_STATUS_LEN); while (::memcmp(data, DVAP_STATUS, 4U) != 0) { + if (m_stopped) + return; + unsigned char c; int n = m_serial.read(&c, 1U); - if (n > 0) { - data[0U] = data[1U]; - data[1U] = data[2U]; - data[2U] = data[3U]; - data[3U] = data[4U]; - data[4U] = data[5U]; - data[5U] = data[6U]; - data[6U] = c; - - // CUtils::dump(wxT("Resync buffer"), data, DVAP_STATUS_LEN); + if (n < 0) + return; + if (n == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + continue; } + + data[0U] = data[1U]; + data[1U] = data[2U]; + data[2U] = data[3U]; + data[3U] = data[4U]; + data[4U] = data[5U]; + data[5U] = data[6U]; + data[6U] = c; + + // CUtils::dump("Resync buffer", data, DVAP_STATUS_LEN); } - wxLogMessage(wxT("End resynchronising")); + wxLogMessage("End resynchronising"); } #if defined(DVAP_DUMP) @@ -1004,22 +1037,15 @@ void CDVAPController::storePacket(const unsigned char* data, unsigned int length void CDVAPController::dumpPackets() { - unsigned int n = m_dvapIndex; - unsigned int i = 0U; - while (n != m_dvapIndex) { + unsigned int n = (m_dvapIndex + 1U) % DVAP_DUMP_LENGTH; + for (unsigned int i = 0U; i < DVAP_DUMP_LENGTH; i++) { if (m_dvapLength[n] > 0U) { - wxString text; - text.Printf(wxT("Packet: %u"), i); - + char text[32]; + ::snprintf(text, sizeof(text), "Packet: %u", i); CUtils::dump(text, m_dvapData[n], m_dvapLength[n]); } - - i++; - n++; - if (n >= DVAP_DUMP_LENGTH) - n = 0U; + n = (n + 1U) % DVAP_DUMP_LENGTH; } - m_dvapIndex = 0U; } diff --git a/Common/DVAPController.h b/Common/DVAPController.h index 0e5881b..e92d95f 100644 --- a/Common/DVAPController.h +++ b/Common/DVAPController.h @@ -26,8 +26,10 @@ #include "HeaderData.h" #include "Modem.h" #include "Utils.h" +#include "StdCompat.h" -#include +#include +#include enum RESP_TYPE { RT_TIMEOUT, @@ -53,13 +55,44 @@ enum RESP_TYPE { RT_FM_DATA }; +/* + * CDVAPController - Driver for the DVAP USB dongle (Digital Voice Access Point). + * + * Hardware interface: USB CDC-ACM serial at 230400 baud. + * + * Protocol: Binary, little-endian framing. + * Every frame begins with a 2-byte header: + * byte[0] = total frame length (low byte) + * byte[1] = flags in upper 3 bits; length high bits in lower 5 + * The command/type is encoded in bytes[2..3]. + * + * Startup sequence (enforced in start()): + * getName() -> getFirmware() -> getSerial() -> + * setModulation() -> setMode() -> setSquelch() -> setPower() -> setFrequency() -> + * startDVAP() + * + * During operation (entry() thread): + * - Polls every ~2s by sending a 3-byte ACK (writePoll()). + * - The DVAP sends RT_STATE packets every ~20ms containing RSSI, squelch + * state, and TX buffer space; these are used to clock outbound data writes. + * - Received headers and GMSK voice frames are forwarded to m_rxData (the + * CModem shared RX ring buffer) as DSMTT_HEADER / DSMTT_DATA / DSMTT_EOT + * typed records. + * - If the DVAP spontaneously sends an FM_DATA packet it has lost GMSK lock; + * the driver restarts modulation to recover. + * + * Power: -12 to +10 dBm (int16_t, little-endian in wire frame). + * Squelch: -128 to -45 (int8_t, signed dBm threshold). + * Frequency: 32-bit Hz, validated against dongle-reported hardware limits. + * + * DVAP_DUMP build flag enables a circular packet trace buffer (last 30 frames) + * that is dumped to the log on any error or unexpected FM mode event. + */ class CDVAPController : public CModem { public: - CDVAPController(const wxString& port, unsigned int frequency, int power, int squelch); + CDVAPController(const std::string& port, unsigned int frequency, int power, int squelch); virtual ~CDVAPController(); - virtual void* Entry(); - virtual bool start(); virtual unsigned int getSpace(); @@ -72,16 +105,18 @@ public: virtual bool writeData(const unsigned char* data, unsigned int length, bool end); private: + void entry(); + CSerialDataController m_serial; - wxUint32 m_frequency; - wxInt16 m_power; - wxInt8 m_squelch; - bool m_squelchOpen; - int m_signal; + uint32_t m_frequency; + int16_t m_power; + int8_t m_squelch; + std::atomic m_squelchOpen; + std::atomic m_signal; unsigned char* m_buffer; - wxUint16 m_streamId; - wxUint8 m_framePos; - wxUint8 m_seq; + uint16_t m_streamId; + uint8_t m_framePos; + uint8_t m_seq; CRingBuffer m_txData; #if defined(DVAP_DUMP) unsigned char** m_dvapData; @@ -103,10 +138,17 @@ private: bool startDVAP(); bool stopDVAP(); + // Sends a 3-byte keepalive ACK; DVAP requires periodic traffic to stay active. void writePoll(); + // Resynchronises the byte stream after a framing error by discarding bytes + // one at a time until a complete RT_STATE (status) header is found. + // Called when getResponse() receives an unrecognisable or oversized frame. void resync(); + // Reads one complete binary frame from the serial port into buffer. + // Returns a RESP_TYPE identifying the frame kind, or RT_TIMEOUT if no + // data is available and RT_ERROR on a read failure. RESP_TYPE getResponse(unsigned char* buffer, unsigned int& length); #if defined(DVAP_DUMP) diff --git a/Common/DVMegaController.cpp b/Common/DVMegaController.cpp index 73df6eb..a306268 100644 --- a/Common/DVMegaController.cpp +++ b/Common/DVMegaController.cpp @@ -20,13 +20,19 @@ #include "DVMegaController.h" #include "CCITTChecksum.h" #include "DStarDefines.h" +#include "Logger.h" #include "Timer.h" #include "Utils.h" -#if defined(__WINDOWS__) -#include -#else -#include +#include +#include +#include +#include +#include +#include "EndianCompat.h" +#if !defined(_WIN32) +#include +#include #endif const unsigned char DVRPTR_HEADER_LENGTH = 5U; @@ -57,7 +63,7 @@ const unsigned int MAX_RESPONSES = 30U; const unsigned int BUFFER_LENGTH = 200U; -CDVMegaController::CDVMegaController(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, unsigned int txDelay) : +CDVMegaController::CDVMegaController(const std::string& port, const std::string& path, bool rxInvert, bool txInvert, unsigned int txDelay) : CModem(), m_port(port), m_path(path), @@ -68,7 +74,7 @@ m_rxFrequency(0U), m_txFrequency(0U), m_power(0U), m_serial(port, SERIAL_115200, true), -m_buffer(NULL), +m_buffer(nullptr), m_txData(1000U), m_txCounter(0U), m_pktCounter(0U), @@ -77,12 +83,12 @@ m_txSpace(0U), m_txEnabled(false), m_checksum(false) { - wxASSERT(!port.IsEmpty()); + assert(!port.empty()); m_buffer = new unsigned char[BUFFER_LENGTH]; } -CDVMegaController::CDVMegaController(const wxString& port, const wxString& path, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power) : +CDVMegaController::CDVMegaController(const std::string& port, const std::string& path, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power) : CModem(), m_port(port), m_path(path), @@ -93,7 +99,7 @@ m_rxFrequency(rxFrequency), m_txFrequency(txFrequency), m_power(power), m_serial(port, SERIAL_115200, true), -m_buffer(NULL), +m_buffer(nullptr), m_txData(1000U), m_txCounter(0U), m_pktCounter(0U), @@ -102,11 +108,11 @@ m_txSpace(0U), m_txEnabled(false), m_checksum(false) { - wxASSERT(!port.IsEmpty()); - wxASSERT((rxFrequency >= 144000000U && rxFrequency <= 148000000U) || - (rxFrequency >= 420000000U && rxFrequency <= 450000000U)); - wxASSERT((txFrequency >= 144000000U && txFrequency <= 148000000U) || - (txFrequency >= 420000000U && txFrequency <= 450000000U)); + assert(!port.empty()); + assert((rxFrequency >= 144000000U && rxFrequency <= 148000000U) || + (rxFrequency >= 420000000U && rxFrequency <= 450000000U)); + assert((txFrequency >= 144000000U && txFrequency <= 148000000U) || + (txFrequency >= 420000000U && txFrequency <= 450000000U)); m_buffer = new unsigned char[BUFFER_LENGTH]; } @@ -126,16 +132,14 @@ bool CDVMegaController::start() findPath(); - Create(); - SetPriority(100U); - Run(); + m_thread = std::thread(&CDVMegaController::entry, this); return true; } -void* CDVMegaController::Entry() +void CDVMegaController::entry() { - wxLogMessage(wxT("Starting DVMEGA Controller thread")); + wxLogMessage("Starting DVMEGA Controller thread"); // Clock every 5ms-ish CTimer pollTimer(200U, 0U, 100U); @@ -154,8 +158,9 @@ void* CDVMegaController::Entry() if (!ret) { ret = findModem(); if (!ret) { - wxLogMessage(wxT("Stopping DVMEGA Controller thread")); - return NULL; + wxLogMessage("Stopping DVMEGA Controller thread"); + delete[] writeBuffer; + return; } } @@ -172,29 +177,30 @@ void* CDVMegaController::Entry() case RTM_ERROR: { bool ret = findModem(); if (!ret) { - wxLogMessage(wxT("Stopping DVMEGA Controller thread")); - return NULL; + wxLogMessage("Stopping DVMEGA Controller thread"); + delete[] writeBuffer; + return; } } break; case RTM_RXPREAMBLE: - // wxLogMessage(wxT("RT_PREAMBLE")); + // wxLogMessage("RT_PREAMBLE"); break; case RTM_START: - // wxLogMessage(wxT("RT_START")); + // wxLogMessage("RT_START"); break; case RTM_HEADER: - CUtils::dump(wxT("RT_HEADER"), m_buffer, length); + CUtils::dump("RT_HEADER", m_buffer, length); if (length == 7U) { if (m_buffer[4U] == DVRPTR_NAK) - wxLogWarning(wxT("Received a header NAK from the DVMEGA")); + wxLogWarning("Received a header NAK from the DVMEGA"); } else { bool correct = (m_buffer[5U] & 0x80U) == 0x00U; if (correct) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_HEADER; @@ -209,16 +215,16 @@ void* CDVMegaController::Entry() break; case RTM_RXSYNC: - // wxLogMessage(wxT("RT_RXSYNC")); + // wxLogMessage("RT_RXSYNC"); break; case RTM_DATA: - CUtils::dump(wxT("RT_DATA"), m_buffer, length); + CUtils::dump("RT_DATA", m_buffer, length); if (length == 7U) { if (m_buffer[4U] == DVRPTR_NAK) - wxLogWarning(wxT("Received a data NAK from the DVMEGA")); + wxLogWarning("Received a data NAK from the DVMEGA"); } else { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_DATA; @@ -232,8 +238,8 @@ void* CDVMegaController::Entry() break; case RTM_EOT: { - // wxLogMessage(wxT("RT_EOT")); - wxMutexLocker locker(m_mutex); + // wxLogMessage("RT_EOT"); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; @@ -245,8 +251,8 @@ void* CDVMegaController::Entry() break; case RTM_RXLOST: { - // wxLogMessage(wxT("RT_LOST")); - wxMutexLocker locker(m_mutex); + // wxLogMessage("RT_LOST"); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_LOST; @@ -263,8 +269,8 @@ void* CDVMegaController::Entry() m_tx = (m_buffer[5U] & 0x02U) == 0x02U; m_txSpace = m_buffer[8U]; space = m_txSpace - m_buffer[9U]; - // CUtils::dump(wxT("GET_STATUS"), m_buffer, length); - // wxLogMessage(wxT("PTT=%d tx=%u space=%u cksum=%d, tx enabled=%d"), int(m_tx), m_txSpace, space, int(m_checksum), int(m_txEnabled)); + // CUtils::dump("GET_STATUS", m_buffer, length); + // wxLogMessage("PTT=%d tx=%u space=%u cksum=%d, tx enabled=%d", int(m_tx), m_txSpace, space, int(m_checksum), int(m_txEnabled)); } break; @@ -275,14 +281,14 @@ void* CDVMegaController::Entry() break; default: - wxLogMessage(wxT("Unknown message, type: %02X"), m_buffer[3U]); - CUtils::dump(wxT("Buffer dump"), m_buffer, length); + wxLogMessage("Unknown message, type: %02X", m_buffer[3U]); + CUtils::dump("Buffer dump", m_buffer, length); break; } if (space > 0U) { if (writeType == DSMTT_NONE && m_txData.hasData()) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); m_txData.getData(&writeType, 1U); m_txData.getData(&writeLength, 1U); @@ -291,53 +297,51 @@ void* CDVMegaController::Entry() // Only send the start when the TX is off if (!m_tx && writeType == DSMTT_START) { - // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + // CUtils::dump("Write Header", writeBuffer, writeLength); int ret = m_serial.write(writeBuffer, writeLength); if (ret != int(writeLength)) - wxLogWarning(wxT("Error when writing the header to the DVMEGA")); + wxLogWarning("Error when writing the header to the DVMEGA"); writeType = DSMTT_NONE; space--; } if (space > 4U && writeType == DSMTT_HEADER) { - // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + // CUtils::dump("Write Header", writeBuffer, writeLength); int ret = m_serial.write(writeBuffer, writeLength); if (ret != int(writeLength)) - wxLogWarning(wxT("Error when writing the header to the DVMEGA")); + wxLogWarning("Error when writing the header to the DVMEGA"); writeType = DSMTT_NONE; space -= 4U; } if (writeType == DSMTT_DATA || writeType == DSMTT_EOT) { - // CUtils::dump(wxT("Write Data"), writeBuffer, writeLength); + // CUtils::dump("Write Data", writeBuffer, writeLength); int ret = m_serial.write(writeBuffer, writeLength); if (ret != int(writeLength)) - wxLogWarning(wxT("Error when writing data to the DVMEGA")); + wxLogWarning("Error when writing data to the DVMEGA"); writeType = DSMTT_NONE; space--; } } - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); pollTimer.clock(); } - wxLogMessage(wxT("Stopping DVMEGA Controller thread")); + wxLogMessage("Stopping DVMEGA Controller thread"); setEnabled(false); delete[] writeBuffer; m_serial.close(); - - return NULL; } bool CDVMegaController::writeHeader(const CHeaderData& header) @@ -347,7 +351,7 @@ bool CDVMegaController::writeHeader(const CHeaderData& header) bool ret = m_txData.hasSpace(64U); if (!ret) { - wxLogWarning(wxT("No space to write the header")); + wxLogWarning("No space to write the header"); return false; } @@ -397,25 +401,25 @@ bool CDVMegaController::writeHeader(const CHeaderData& header) buffer2[9U] = header.getFlag2(); buffer2[10U] = header.getFlag3(); - wxString rpt2 = header.getRptCall2(); - for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer2[i + 11U] = rpt2.GetChar(i); + std::string rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 11U] = rpt2[i]; - wxString rpt1 = header.getRptCall1(); - for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer2[i + 19U] = rpt1.GetChar(i); + std::string rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 19U] = rpt1[i]; - wxString your = header.getYourCall(); - for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer2[i + 27U] = your.GetChar(i); + std::string your = header.getYourCall(); + for (unsigned int i = 0U; i < your.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 27U] = your[i]; - wxString my1 = header.getMyCall1(); - for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer2[i + 35U] = my1.GetChar(i); + std::string my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 35U] = my1[i]; - wxString my2 = header.getMyCall2(); - for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) - buffer2[i + 43U] = my2.GetChar(i); + std::string my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.size() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer2[i + 43U] = my2[i]; CCCITTChecksumReverse cksum1; cksum1.update(buffer2 + 8U, RADIO_HEADER_LENGTH_BYTES - 2U); @@ -434,7 +438,7 @@ bool CDVMegaController::writeHeader(const CHeaderData& header) m_pktCounter = 0U; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char type1 = DSMTT_START; m_txData.addData(&type1, 1U); @@ -462,7 +466,7 @@ bool CDVMegaController::writeData(const unsigned char* data, unsigned int, bool bool ret = m_txData.hasSpace(26U); if (!ret) { - wxLogWarning(wxT("No space to write data")); + wxLogWarning("No space to write data"); return false; } @@ -488,7 +492,7 @@ bool CDVMegaController::writeData(const unsigned char* data, unsigned int, bool buffer[7U] = 0x0BU; } - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char type = DSMTT_EOT; m_txData.addData(&type, 1U); @@ -532,7 +536,7 @@ bool CDVMegaController::writeData(const unsigned char* data, unsigned int, bool buffer[23U] = 0x0BU; } - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char type = DSMTT_DATA; m_txData.addData(&type, 1U); @@ -579,36 +583,43 @@ bool CDVMegaController::readVersion() buffer[5U] = 0x0BU; } - // CUtils::dump(wxT("Written"), buffer, 6U); + // CUtils::dump("Written", buffer, 6U); int ret = m_serial.write(buffer, 6U); if (ret != 6) return false; for (unsigned int count = 0U; count < MAX_RESPONSES; count++) { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); unsigned int length; RESP_TYPE_MEGA resp = getResponse(m_buffer, length); if (resp == RTM_GET_VERSION) { - wxString firmware; + char firmware[32]; if ((m_buffer[4U] & 0x0FU) > 0x00U) - firmware.Printf(wxT("%u.%u%u%c"), (m_buffer[5U] & 0xF0U) >> 4, m_buffer[5U] & 0x0FU, (m_buffer[4U] & 0xF0U) >> 4, (m_buffer[4U] & 0x0FU) + wxT('a') - 1U); + ::snprintf(firmware, sizeof(firmware), "%u.%u%u%c", + (m_buffer[5U] & 0xF0U) >> 4, + m_buffer[5U] & 0x0FU, + (m_buffer[4U] & 0xF0U) >> 4, + (m_buffer[4U] & 0x0FU) + 'a' - 1U); else - firmware.Printf(wxT("%u.%u%u"), (m_buffer[5U] & 0xF0U) >> 4, m_buffer[5U] & 0x0FU, (m_buffer[4U] & 0xF0U) >> 4); + ::snprintf(firmware, sizeof(firmware), "%u.%u%u", + (m_buffer[5U] & 0xF0U) >> 4, + m_buffer[5U] & 0x0FU, + (m_buffer[4U] & 0xF0U) >> 4); - wxString hardware((char*)(m_buffer + 6U), wxConvLocal, length - DVRPTR_HEADER_LENGTH - 3U); + std::string hardware((char*)(m_buffer + 6U), length - DVRPTR_HEADER_LENGTH - 3U); - wxLogInfo(wxT("DVMEGA Firmware version: %s, hardware: %s"), firmware.c_str(), hardware.c_str()); + wxLogInfo("DVMEGA Firmware version: %s, hardware: %s", firmware, hardware.c_str()); return true; } } - ::wxSleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); } - wxLogError(wxT("Unable to read the firmware version after six attempts")); + wxLogError("Unable to read the firmware version after six attempts"); return false; } @@ -657,8 +668,8 @@ bool CDVMegaController::setConfig() if (m_txInvert) buffer[6U] |= 0x02U; - wxUint16* txDelay = (wxUint16*)(buffer + 8U); - *txDelay = wxUINT16_SWAP_ON_BE(m_txDelay); + uint16_t txDelay = htole16((uint16_t)m_txDelay); + ::memcpy(buffer + 8U, &txDelay, sizeof(uint16_t)); if (m_checksum) { CCCITTChecksum cksum; @@ -669,7 +680,7 @@ bool CDVMegaController::setConfig() buffer[11U] = 0x0BU; } - // CUtils::dump(wxT("Written"), buffer, 12U); + // CUtils::dump("Written", buffer, 12U); int ret = m_serial.write(buffer, 12U); if (ret != 12) @@ -680,24 +691,24 @@ bool CDVMegaController::setConfig() RESP_TYPE_MEGA resp; do { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); resp = getResponse(m_buffer, length); if (resp != RTM_SET_CONFIG) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVMEGA is not responding to the SET_CONFIG command")); + wxLogError("The DVMEGA is not responding to the SET_CONFIG command"); return false; } } } while (resp != RTM_SET_CONFIG); - // CUtils::dump(wxT("Response"), m_buffer, length); + // CUtils::dump("Response", m_buffer, length); unsigned char type = m_buffer[4U]; if (type != DVRPTR_ACK) { - wxLogError(wxT("Received a NAK to the SET_CONFIG command from the modem")); + wxLogError("Received a NAK to the SET_CONFIG command from the modem"); return false; } @@ -721,11 +732,11 @@ bool CDVMegaController::setFrequencyAndPower() buffer[5U] = 0x0CU; // Block length - wxUint32 rxFreq = wxUINT32_SWAP_ON_BE(wxUint32(m_rxFrequency)); - wxUint32 txFreq = wxUINT32_SWAP_ON_BE(wxUint32(m_txFrequency)); + uint32_t rxFreq = htole32((uint32_t)m_rxFrequency); + uint32_t txFreq = htole32((uint32_t)m_txFrequency); - ::memcpy(buffer + 7U, &rxFreq, sizeof(wxUint32)); - ::memcpy(buffer + 11U, &txFreq, sizeof(wxUint32)); + ::memcpy(buffer + 7U, &rxFreq, sizeof(uint32_t)); + ::memcpy(buffer + 11U, &txFreq, sizeof(uint32_t)); buffer[16U] = (m_power * 64U) / 100U; @@ -738,7 +749,7 @@ bool CDVMegaController::setFrequencyAndPower() buffer[20U] = 0x0BU; } - // CUtils::dump(wxT("Written"), buffer, 21U); + // CUtils::dump("Written", buffer, 21U); int ret = m_serial.write(buffer, 21U); if (ret != 21) @@ -749,24 +760,24 @@ bool CDVMegaController::setFrequencyAndPower() RESP_TYPE_MEGA resp; do { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); resp = getResponse(m_buffer, length); if (resp != RTM_SET_CONFIG) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVMEGA is not responding to the SET_CONFIG command")); + wxLogError("The DVMEGA is not responding to the SET_CONFIG command"); return false; } } } while (resp != RTM_SET_CONFIG); - // CUtils::dump(wxT("Response"), m_buffer, length); + // CUtils::dump("Response", m_buffer, length); unsigned char type = m_buffer[4U]; if (type != DVRPTR_ACK) { - wxLogError(wxT("Received a NAK to the SET_CONFIG command from the modem")); + wxLogError("Received a NAK to the SET_CONFIG command from the modem"); return false; } @@ -799,7 +810,7 @@ bool CDVMegaController::setEnabled(bool enable) buffer[6U] = 0x0BU; } - // CUtils::dump(wxT("Written"), buffer, 7U); + // CUtils::dump("Written", buffer, 7U); int ret = m_serial.write(buffer, 7U); if (ret != 7) @@ -809,36 +820,41 @@ bool CDVMegaController::setEnabled(bool enable) unsigned int length; RESP_TYPE_MEGA resp; do { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); resp = getResponse(m_buffer, length); if (resp != RTM_GET_STATUS) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DVMEGA is not responding to the SET_STATUS command")); + wxLogError("The DVMEGA is not responding to the SET_STATUS command"); return false; } } } while (resp != RTM_GET_STATUS); - // CUtils::dump(wxT("Response"), m_buffer, length); + // CUtils::dump("Response", m_buffer, length); unsigned char type = m_buffer[4U]; if (type != DVRPTR_ACK) { - wxLogError(wxT("Received a NAK to the SET_STATUS command from the modem")); + wxLogError("Received a NAK to the SET_STATUS command from the modem"); return false; } return true; } +// Reads one complete DVRPTR frame. +// Frame header (5 bytes): [0xD0] [len_lo] [len_hi] [type] [...] +// length = header bytes 1+2 (payload length, not including header itself). +// type has bit 7 set for board responses; strip it before dispatching. +// Returns RTM_TIMEOUT if no start byte is available, RTM_ERROR on I/O failure. RESP_TYPE_MEGA CDVMegaController::getResponse(unsigned char *buffer, unsigned int& length) { // Get the start of the frame or nothing at all int ret = m_serial.read(buffer, 1U); if (ret < 0) { - wxLogError(wxT("Error when reading from the DVMEGA")); + wxLogError("Error when reading from the DVMEGA"); return RTM_ERROR; } @@ -853,21 +869,24 @@ RESP_TYPE_MEGA CDVMegaController::getResponse(unsigned char *buffer, unsigned in while (offset < DVRPTR_HEADER_LENGTH) { ret = m_serial.read(buffer + offset, DVRPTR_HEADER_LENGTH - offset); if (ret < 0) { - wxLogError(wxT("Error when reading from the DVMEGA")); + wxLogError("Error when reading from the DVMEGA"); return RTM_ERROR; } if (ret > 0) offset += ret; - if (ret == 0) - Sleep(5UL); + if (ret == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + if (m_stopped) + return RTM_TIMEOUT; + } } length = buffer[1U] + buffer[2U] * 256U; if (length >= 100U) { - wxLogError(wxT("Invalid data received from the DVMEGA")); + wxLogError("Invalid data received from the DVMEGA"); return RTM_ERROR; } @@ -879,20 +898,23 @@ RESP_TYPE_MEGA CDVMegaController::getResponse(unsigned char *buffer, unsigned in while (offset < length) { ret = m_serial.read(buffer + offset + DVRPTR_HEADER_LENGTH, length - offset); if (ret < 0) { - wxLogError(wxT("Error when reading from the DVMEGA")); + wxLogError("Error when reading from the DVMEGA"); return RTM_ERROR; } if (ret > 0) offset += ret; - if (ret == 0) - Sleep(5UL); + if (ret == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + if (m_stopped) + return RTM_TIMEOUT; + } } length += DVRPTR_HEADER_LENGTH; - // CUtils::dump(wxT("Received"), buffer, length); + // CUtils::dump("Received", buffer, length); switch (type) { case DVRPTR_GET_STATUS: @@ -926,149 +948,100 @@ RESP_TYPE_MEGA CDVMegaController::getResponse(unsigned char *buffer, unsigned in } } -wxString CDVMegaController::getPath() const +std::string CDVMegaController::getPath() const { return m_path; } bool CDVMegaController::findPort() { - if (m_path.IsEmpty()) + if (m_path.empty()) return false; -#if defined(__WINDOWS__) -#else - wxDir dir; - bool ret1 = dir.Open(wxT("/sys/class/tty")); - if (!ret1) { - wxLogError(wxT("Cannot open directory /sys/class/tty")); + DIR* dir = ::opendir("/sys/class/tty"); + if (dir == nullptr) { + wxLogError("Cannot open directory /sys/class/tty"); return false; } - wxString fileName; - ret1 = dir.GetFirst(&fileName, wxT("ttyACM*")); - while (ret1) { - wxString path; - path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str()); + struct dirent* entry; + while ((entry = ::readdir(dir)) != nullptr) { + std::string fileName(entry->d_name); - char cpath[255U]; - ::memset(cpath, 0x00U, 255U); + // Match ttyACM* entries + if (fileName.substr(0, 6) != "ttyACM") + continue; - for (unsigned int i = 0U; i < path.Len(); i++) - cpath[i] = path.GetChar(i); + std::string path = "/sys/class/tty/" + fileName; + + char cpath[255U]; + ::strncpy(cpath, path.c_str(), sizeof(cpath) - 1); + cpath[sizeof(cpath) - 1] = '\0'; char symlink[255U]; - int ret2 = ::readlink(cpath, symlink, 255U); + int ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret2 < 0) { - ::strcat(cpath, "/device"); - ret2 = ::readlink(cpath, symlink, 255U); + ::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1); + ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret2 < 0) { - wxLogError(wxT("Error from readlink()")); + wxLogError("Error from readlink()"); + ::closedir(dir); return false; } - - path = wxString(symlink, wxConvLocal, ret2); + symlink[ret2] = '\0'; + path = std::string(symlink, ret2); } else { + symlink[ret2] = '\0'; + std::string fullPath(symlink, ret2); // Get all but the last section - wxString fullPath = wxString(symlink, wxConvLocal, ret2); - path = fullPath.BeforeLast(wxT('/')); + size_t pos = fullPath.rfind('/'); + path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath; } - if (path.IsSameAs(m_path)) { - m_port.Printf(wxT("/dev/%s"), fileName.c_str()); + if (path == m_path) { + m_port = "/dev/" + fileName; - wxLogMessage(wxT("Found modem port of %s based on the path"), m_port.c_str()); + wxLogMessage("Found modem port of %s based on the path", m_port.c_str()); + ::closedir(dir); return true; } - - ret1 = dir.GetNext(&fileName); } -#endif + ::closedir(dir); return false; } bool CDVMegaController::findPath() { -#if defined(__WINDOWS__) -#ifdef notdef - GUID guids[5U]; - - DWORD count; - BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count); - if (!res) { - wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError()); - return false; - } - - for (DWORD i = 0U; i < count; i++) { - HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); - if (devInfo == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); - return false; - } - - SP_DEVICE_INTERFACE_DATA devInfoData; - devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - - for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) { - // Find the required length of the device structure - DWORD length; - ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); - - PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); - detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - // Get the detailed data into the newly allocated device structure - DWORD required; - res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); - if (!res) { - wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - ::free(detailData); - } - - ::SetupDiDestroyDeviceInfoList(devInfo); - } - - return false; -#endif -#else - wxString path; - path.Printf(wxT("/sys/class/tty/%s"), m_port.Mid(5U).c_str()); + std::string path = "/sys/class/tty/" + m_port.substr(5U); char cpath[255U]; - ::memset(cpath, 0x00U, 255U); - - for (unsigned int i = 0U; i < path.Len(); i++) - cpath[i] = path.GetChar(i); + ::strncpy(cpath, path.c_str(), sizeof(cpath) - 1); + cpath[sizeof(cpath) - 1] = '\0'; char symlink[255U]; - int ret = ::readlink(cpath, symlink, 255U); + int ret = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret < 0) { - ::strcat(cpath, "/device"); - ret = ::readlink(cpath, symlink, 255U); + ::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1); + ret = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret < 0) { - wxLogError(wxT("Error from readlink()")); + wxLogError("Error from readlink()"); return false; } - - path = wxString(symlink, wxConvLocal, ret); + symlink[ret] = '\0'; + path = std::string(symlink, ret); } else { - wxString fullPath = wxString(symlink, wxConvLocal, ret); - path = fullPath.BeforeLast(wxT('/')); + symlink[ret] = '\0'; + std::string fullPath(symlink, ret); + size_t pos = fullPath.rfind('/'); + path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath; } - if (m_path.IsEmpty()) - wxLogMessage(wxT("Found modem path of %s"), path.c_str()); + if (m_path.empty()) + wxLogMessage("Found modem path of %s", path.c_str()); m_path = path; -#endif return true; } @@ -1079,7 +1052,7 @@ bool CDVMegaController::findModem() // Tell the repeater that the signal has gone away if (m_rx) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; @@ -1095,7 +1068,7 @@ bool CDVMegaController::findModem() while (!m_stopped) { count++; if (count >= 4U) { - wxLogMessage(wxT("Trying to reopen the modem")); + wxLogMessage("Trying to reopen the modem"); bool ret = findPort(); if (ret) { @@ -1107,7 +1080,7 @@ bool CDVMegaController::findModem() count = 0U; } - Sleep(500UL); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); } return false; @@ -1147,4 +1120,3 @@ bool CDVMegaController::openModem() return true; } - diff --git a/Common/DVMegaController.h b/Common/DVMegaController.h index 9ae29ee..b129e14 100644 --- a/Common/DVMegaController.h +++ b/Common/DVMegaController.h @@ -23,8 +23,9 @@ #include "RingBuffer.h" #include "Modem.h" #include "Utils.h" +#include "StdCompat.h" -#include +#include enum RESP_TYPE_MEGA { RTM_TIMEOUT, @@ -45,14 +46,47 @@ enum RESP_TYPE_MEGA { RTM_SET_TESTMDE }; +/* + * CDVMegaController - Driver for DV-Mega boards (modem and radio variants). + * + * Hardware interface: USB CDC-ACM serial at 115200 baud. + * + * Two hardware variants, selected by constructor: + * 1. Modem variant (port, path, rxInvert, txInvert, txDelay) + * The board is a bare GMSK modem; the host radio provides the RF. + * rxInvert/txInvert flip the baseband polarity to match the radio's + * discriminator/modulator wiring. + * 2. Radio variant (port, path, txDelay, rxFrequency, txFrequency, power) + * The board includes a UHF/VHF transceiver. Frequency and power are + * programmed at startup via a second SET_CONFIG (RF layer) command. + * rxFrequency != 0 is the distinguishing condition checked in openModem(). + * + * Protocol: DVRPTR binary framing (shared with DV-RPTR V1). + * Frame structure: [0xD0] [len_lo] [len_hi] [type] [txCount] [pktCount] + * [payload...] [checksum_lo] [checksum_hi] + * The type byte has bit 7 set in responses from the board (stripped with & 0x7F). + * Checksum is either CCITT-16 (when m_checksum is true, reported in GET_STATUS) + * or a fixed sentinel 0x000B when the firmware does not support checksums. + * + * Reconnection (findModem()): + * On any serial error the port is closed. An in-progress RX stream is + * terminated with a synthetic DSMTT_EOT. The driver then polls every 2s + * (4 x 500ms sleep) searching for the device by USB sysfs path before + * reopening and re-initialising. + * + * Sysfs path tracking (findPort / findPath): + * m_path is the stable USB device path (e.g. /sys/devices/platform/.../1-1.2). + * This survives port renumbering across reconnects. findPort() walks + * /sys/class/tty/ttyACM* to find which /dev node currently maps to that path. + */ class CDVMegaController : public CModem { public: - CDVMegaController(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, unsigned int txDelay); - CDVMegaController(const wxString& port, const wxString& path, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power); + // Modem-only variant: no integrated RF, host radio provides the signal. + CDVMegaController(const std::string& port, const std::string& path, bool rxInvert, bool txInvert, unsigned int txDelay); + // Radio variant: integrated transceiver; frequency in Hz, power in percent. + CDVMegaController(const std::string& port, const std::string& path, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power); virtual ~CDVMegaController(); - virtual void* Entry(); - virtual bool start(); virtual unsigned int getSpace(); @@ -61,11 +95,13 @@ public: virtual bool writeHeader(const CHeaderData& header); virtual bool writeData(const unsigned char* data, unsigned int length, bool end); - virtual wxString getPath() const; + virtual std::string getPath() const; private: - wxString m_port; - wxString m_path; + void entry(); + + std::string m_port; + std::string m_path; bool m_rxInvert; bool m_txInvert; unsigned int m_txDelay; @@ -93,9 +129,12 @@ private: bool findPort(); bool findPath(); + // Closes the port, signals EOT if mid-stream, then polls every 2s until + // findPort() + openModem() succeed or m_stopped is set. bool findModem(); + // Opens the serial port, sends GET_VERSION, SET_CONFIG (physical layer), + // optionally SET_CONFIG (RF layer for radio variant), then setEnabled(true). bool openModem(); }; #endif - diff --git a/Common/DVRPTRV1Controller.cpp b/Common/DVRPTRV1Controller.cpp index f0cbff1..176d97d 100644 --- a/Common/DVRPTRV1Controller.cpp +++ b/Common/DVRPTRV1Controller.cpp @@ -20,12 +20,18 @@ #include "DVRPTRV1Controller.h" #include "CCITTChecksum.h" #include "DStarDefines.h" +#include "Logger.h" #include "Timer.h" -#if defined(__WINDOWS__) -#include -#else -#include +#include +#include +#include +#include +#include +#include "EndianCompat.h" +#if !defined(_WIN32) +#include +#include #endif const unsigned char DVRPTR_HEADER_LENGTH = 5U; @@ -56,7 +62,7 @@ const unsigned int MAX_RESPONSES = 30U; const unsigned int BUFFER_LENGTH = 200U; -CDVRPTRV1Controller::CDVRPTRV1Controller(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay) : +CDVRPTRV1Controller::CDVRPTRV1Controller(const std::string& port, const std::string& path, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay) : CModem(), m_port(port), m_path(path), @@ -66,7 +72,7 @@ m_channel(channel), m_modLevel(modLevel), m_txDelay(txDelay), m_serial(port, SERIAL_115200), -m_buffer(NULL), +m_buffer(nullptr), m_txData(1000U), m_txCounter(0U), m_pktCounter(0U), @@ -75,7 +81,7 @@ m_txSpace(0U), m_txEnabled(false), m_checksum(false) { - wxASSERT(!port.IsEmpty()); + assert(!port.empty()); m_buffer = new unsigned char[BUFFER_LENGTH]; } @@ -95,16 +101,14 @@ bool CDVRPTRV1Controller::start() findPath(); - Create(); - SetPriority(100U); - Run(); + m_thread = std::thread(&CDVRPTRV1Controller::entry, this); return true; } -void* CDVRPTRV1Controller::Entry() +void CDVRPTRV1Controller::entry() { - wxLogMessage(wxT("Starting DV-RPTR1 Modem Controller thread")); + wxLogMessage("Starting DV-RPTR1 Modem Controller thread"); // Clock every 5ms-ish CTimer pollTimer(200U, 0U, 100U); @@ -123,8 +127,9 @@ void* CDVRPTRV1Controller::Entry() if (!ret) { ret = findModem(); if (!ret) { - wxLogMessage(wxT("Stopping DV-RPTR1 Modem Controller thread")); - return NULL; + wxLogMessage("Stopping DV-RPTR1 Modem Controller thread"); + delete[] writeBuffer; + return; } } @@ -141,29 +146,30 @@ void* CDVRPTRV1Controller::Entry() case RT1_ERROR: { bool ret = findModem(); if (!ret) { - wxLogMessage(wxT("Stopping DV-RPTR1 Modem Controller thread")); - return NULL; + wxLogMessage("Stopping DV-RPTR1 Modem Controller thread"); + delete[] writeBuffer; + return; } } break; case RT1_RXPREAMBLE: - // wxLogMessage(wxT("RT_PREAMBLE")); + // wxLogMessage("RT_PREAMBLE"); break; case RT1_START: - // wxLogMessage(wxT("RT_START")); + // wxLogMessage("RT_START"); break; case RT1_HEADER: - // CUtils::dump(wxT("RT_HEADER"), m_buffer, length); + // CUtils::dump("RT_HEADER", m_buffer, length); if (length == 7U) { if (m_buffer[4U] == DVRPTR_NAK) - wxLogWarning(wxT("Received a header NAK from the modem")); + wxLogWarning("Received a header NAK from the modem"); } else { bool correct = (m_buffer[5U] & 0x80U) == 0x00U; if (correct) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_HEADER; @@ -178,16 +184,16 @@ void* CDVRPTRV1Controller::Entry() break; case RT1_RXSYNC: - // wxLogMessage(wxT("RT_RXSYNC")); + // wxLogMessage("RT_RXSYNC"); break; case RT1_DATA: - // CUtils::dump(wxT("RT_DATA"), m_buffer, length); + // CUtils::dump("RT_DATA", m_buffer, length); if (length == 7U) { if (m_buffer[4U] == DVRPTR_NAK) - wxLogWarning(wxT("Received a data NAK from the modem")); + wxLogWarning("Received a data NAK from the modem"); } else { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_DATA; @@ -201,8 +207,8 @@ void* CDVRPTRV1Controller::Entry() break; case RT1_EOT: { - // wxLogMessage(wxT("RT_EOT")); - wxMutexLocker locker(m_mutex); + // wxLogMessage("RT_EOT"); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; @@ -214,8 +220,8 @@ void* CDVRPTRV1Controller::Entry() break; case RT1_RXLOST: { - // wxLogMessage(wxT("RT_LOST")); - wxMutexLocker locker(m_mutex); + // wxLogMessage("RT_LOST"); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_LOST; @@ -232,8 +238,8 @@ void* CDVRPTRV1Controller::Entry() m_tx = (m_buffer[5U] & 0x02U) == 0x02U; m_txSpace = m_buffer[8U]; space = m_txSpace - m_buffer[9U]; - // CUtils::dump(wxT("GET_STATUS"), m_buffer, length); - // wxLogMessage(wxT("PTT=%d tx=%u space=%u cksum=%d, tx enabled=%d"), int(m_tx), m_txSpace, space, int(m_checksum), int(m_txEnabled)); + // CUtils::dump("GET_STATUS", m_buffer, length); + // wxLogMessage("PTT=%d tx=%u space=%u cksum=%d, tx enabled=%d", int(m_tx), m_txSpace, space, int(m_checksum), int(m_txEnabled)); } break; @@ -244,14 +250,14 @@ void* CDVRPTRV1Controller::Entry() break; default: - wxLogMessage(wxT("Unknown message, type: %02X"), m_buffer[3U]); - CUtils::dump(wxT("Buffer dump"), m_buffer, length); + wxLogMessage("Unknown message, type: %02X", m_buffer[3U]); + CUtils::dump("Buffer dump", m_buffer, length); break; } if (space > 0U) { if (writeType == DSMTT_NONE && m_txData.hasData()) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); m_txData.getData(&writeType, 1U); m_txData.getData(&writeLength, 1U); @@ -260,53 +266,51 @@ void* CDVRPTRV1Controller::Entry() // Only send the start when the TX is off if (!m_tx && writeType == DSMTT_START) { - // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + // CUtils::dump("Write Header", writeBuffer, writeLength); int ret = m_serial.write(writeBuffer, writeLength); if (ret != int(writeLength)) - wxLogWarning(wxT("Error when writing the header to the DV-RPTR modem")); + wxLogWarning("Error when writing the header to the DV-RPTR modem"); writeType = DSMTT_NONE; space--; } if (space > 4U && writeType == DSMTT_HEADER) { - // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + // CUtils::dump("Write Header", writeBuffer, writeLength); int ret = m_serial.write(writeBuffer, writeLength); if (ret != int(writeLength)) - wxLogWarning(wxT("Error when writing the header to the DV-RPTR modem")); + wxLogWarning("Error when writing the header to the DV-RPTR modem"); writeType = DSMTT_NONE; space -= 4U; } if (writeType == DSMTT_DATA || writeType == DSMTT_EOT) { - // CUtils::dump(wxT("Write Data"), writeBuffer, writeLength); + // CUtils::dump("Write Data", writeBuffer, writeLength); int ret = m_serial.write(writeBuffer, writeLength); if (ret != int(writeLength)) - wxLogWarning(wxT("Error when writing data to the DV-RPTR modem")); + wxLogWarning("Error when writing data to the DV-RPTR modem"); writeType = DSMTT_NONE; space--; } } - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); pollTimer.clock(); } - wxLogMessage(wxT("Stopping DV-RPTR1 Modem Controller thread")); + wxLogMessage("Stopping DV-RPTR1 Modem Controller thread"); setEnabled(false); delete[] writeBuffer; m_serial.close(); - - return NULL; } bool CDVRPTRV1Controller::writeHeader(const CHeaderData& header) @@ -316,7 +320,7 @@ bool CDVRPTRV1Controller::writeHeader(const CHeaderData& header) bool ret = m_txData.hasSpace(64U); if (!ret) { - wxLogWarning(wxT("No space to write the header")); + wxLogWarning("No space to write the header"); return false; } @@ -366,25 +370,25 @@ bool CDVRPTRV1Controller::writeHeader(const CHeaderData& header) buffer2[9U] = header.getFlag2(); buffer2[10U] = header.getFlag3(); - wxString rpt2 = header.getRptCall2(); - for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer2[i + 11U] = rpt2.GetChar(i); + std::string rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 11U] = rpt2[i]; - wxString rpt1 = header.getRptCall1(); - for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer2[i + 19U] = rpt1.GetChar(i); + std::string rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 19U] = rpt1[i]; - wxString your = header.getYourCall(); - for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer2[i + 27U] = your.GetChar(i); + std::string your = header.getYourCall(); + for (unsigned int i = 0U; i < your.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 27U] = your[i]; - wxString my1 = header.getMyCall1(); - for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer2[i + 35U] = my1.GetChar(i); + std::string my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer2[i + 35U] = my1[i]; - wxString my2 = header.getMyCall2(); - for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) - buffer2[i + 43U] = my2.GetChar(i); + std::string my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.size() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer2[i + 43U] = my2[i]; CCCITTChecksumReverse cksum1; cksum1.update(buffer2 + 8U, RADIO_HEADER_LENGTH_BYTES - 2U); @@ -403,7 +407,7 @@ bool CDVRPTRV1Controller::writeHeader(const CHeaderData& header) m_pktCounter = 0U; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char type1 = DSMTT_START; m_txData.addData(&type1, 1U); @@ -431,7 +435,7 @@ bool CDVRPTRV1Controller::writeData(const unsigned char* data, unsigned int, boo bool ret = m_txData.hasSpace(26U); if (!ret) { - wxLogWarning(wxT("No space to write data")); + wxLogWarning("No space to write data"); return false; } @@ -457,7 +461,7 @@ bool CDVRPTRV1Controller::writeData(const unsigned char* data, unsigned int, boo buffer[7U] = 0x0BU; } - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char type = DSMTT_EOT; m_txData.addData(&type, 1U); @@ -501,7 +505,7 @@ bool CDVRPTRV1Controller::writeData(const unsigned char* data, unsigned int, boo buffer[23U] = 0x0BU; } - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char type = DSMTT_DATA; m_txData.addData(&type, 1U); @@ -547,7 +551,7 @@ bool CDVRPTRV1Controller::readVersion() buffer[5U] = 0x0BU; } - // CUtils::dump(wxT("Written"), buffer, 6U); + // CUtils::dump("Written", buffer, 6U); int ret = m_serial.write(buffer, 6U); if (ret != 6) @@ -557,28 +561,35 @@ bool CDVRPTRV1Controller::readVersion() unsigned int length; RESP_TYPE_V1 resp; do { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); resp = getResponse(m_buffer, length); if (resp != RT1_GET_VERSION) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DV-RPTR modem is not responding to the version command")); + wxLogError("The DV-RPTR modem is not responding to the version command"); return false; } } } while (resp != RT1_GET_VERSION); - wxString firmware; + char firmware[32]; if ((m_buffer[4U] & 0x0FU) > 0x00U) - firmware.Printf(wxT("%u.%u%u%c"), (m_buffer[5U] & 0xF0U) >> 4, m_buffer[5U] & 0x0FU, (m_buffer[4U] & 0xF0U) >> 4, (m_buffer[4U] & 0x0FU) + wxT('a') - 1U); + ::snprintf(firmware, sizeof(firmware), "%u.%u%u%c", + (m_buffer[5U] & 0xF0U) >> 4, + m_buffer[5U] & 0x0FU, + (m_buffer[4U] & 0xF0U) >> 4, + (m_buffer[4U] & 0x0FU) + 'a' - 1U); else - firmware.Printf(wxT("%u.%u%u"), (m_buffer[5U] & 0xF0U) >> 4, m_buffer[5U] & 0x0FU, (m_buffer[4U] & 0xF0U) >> 4); + ::snprintf(firmware, sizeof(firmware), "%u.%u%u", + (m_buffer[5U] & 0xF0U) >> 4, + m_buffer[5U] & 0x0FU, + (m_buffer[4U] & 0xF0U) >> 4); - wxString hardware((char*)(m_buffer + 6U), wxConvLocal, length - DVRPTR_HEADER_LENGTH - 3U); + std::string hardware((char*)(m_buffer + 6U), length - DVRPTR_HEADER_LENGTH - 3U); - wxLogInfo(wxT("DV-RPTR Modem Firmware version: %s, hardware: %s"), firmware.c_str(), hardware.c_str()); + wxLogInfo("DV-RPTR Modem Firmware version: %s, hardware: %s", firmware, hardware.c_str()); return true; } @@ -606,6 +617,10 @@ bool CDVRPTRV1Controller::readStatus() return m_serial.write(buffer, 6U) == 6; } +// Sends SET_CONFIG to the DV-RPTR V1 (physical layer, block type 0xC0). +// buffer[6] flags: bit0 = rxInvert, bit1 = txInvert, bit2 = channel select. +// buffer[7] modLevel: 0-100% mapped to 0-255; controls DAC output amplitude. +// buffer[8..9] txDelay: uint16_t in ms; time between PTT assert and first bit. bool CDVRPTRV1Controller::setConfig() { unsigned char buffer[20U]; @@ -631,8 +646,8 @@ bool CDVRPTRV1Controller::setConfig() buffer[7U] = (m_modLevel * 256U) / 100U; - wxUint16* txDelay = (wxUint16*)(buffer + 8U); - *txDelay = wxUINT16_SWAP_ON_BE(m_txDelay); + uint16_t txDelay = htole16((uint16_t)m_txDelay); + ::memcpy(buffer + 8U, &txDelay, sizeof(uint16_t)); if (m_checksum) { CCCITTChecksum cksum; @@ -643,7 +658,7 @@ bool CDVRPTRV1Controller::setConfig() buffer[11U] = 0x0BU; } - // CUtils::dump(wxT("Written"), buffer, 12U); + // CUtils::dump("Written", buffer, 12U); int ret = m_serial.write(buffer, 12U); if (ret != 12) @@ -654,24 +669,24 @@ bool CDVRPTRV1Controller::setConfig() RESP_TYPE_V1 resp; do { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); resp = getResponse(m_buffer, length); if (resp != RT1_SET_CONFIG) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DV-RPTR modem is not responding to the SET_CONFIG command")); + wxLogError("The DV-RPTR modem is not responding to the SET_CONFIG command"); return false; } } } while (resp != RT1_SET_CONFIG); - // CUtils::dump(wxT("Response"), m_buffer, length); + // CUtils::dump("Response", m_buffer, length); unsigned char type = m_buffer[4U]; if (type != DVRPTR_ACK) { - wxLogError(wxT("Received a NAK to the SET_CONFIG command from the modem")); + wxLogError("Received a NAK to the SET_CONFIG command from the modem"); return false; } @@ -704,7 +719,7 @@ bool CDVRPTRV1Controller::setEnabled(bool enable) buffer[6U] = 0x0BU; } - // CUtils::dump(wxT("Written"), buffer, 7U); + // CUtils::dump("Written", buffer, 7U); int ret = m_serial.write(buffer, 7U); if (ret != 7) @@ -714,24 +729,24 @@ bool CDVRPTRV1Controller::setEnabled(bool enable) unsigned int length; RESP_TYPE_V1 resp; do { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); resp = getResponse(m_buffer, length); if (resp != RT1_GET_STATUS) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DV-RPTR modem is not responding to the SET_STATUS command")); + wxLogError("The DV-RPTR modem is not responding to the SET_STATUS command"); return false; } } } while (resp != RT1_GET_STATUS); - // CUtils::dump(wxT("Response"), m_buffer, length); + // CUtils::dump("Response", m_buffer, length); unsigned char type = m_buffer[4U]; if (type != DVRPTR_ACK) { - wxLogError(wxT("Received a NAK to the SET_STATUS command from the modem")); + wxLogError("Received a NAK to the SET_STATUS command from the modem"); return false; } @@ -743,7 +758,7 @@ RESP_TYPE_V1 CDVRPTRV1Controller::getResponse(unsigned char *buffer, unsigned in // Get the start of the frame or nothing at all int ret = m_serial.read(buffer, 1U); if (ret < 0) { - wxLogError(wxT("Error when reading from the DV-RPTR")); + wxLogError("Error when reading from the DV-RPTR"); return RT1_ERROR; } @@ -758,7 +773,7 @@ RESP_TYPE_V1 CDVRPTRV1Controller::getResponse(unsigned char *buffer, unsigned in while (offset < DVRPTR_HEADER_LENGTH) { ret = m_serial.read(buffer + offset, DVRPTR_HEADER_LENGTH - offset); if (ret < 0) { - wxLogError(wxT("Error when reading from the DV-RPTR")); + wxLogError("Error when reading from the DV-RPTR"); return RT1_ERROR; } @@ -766,7 +781,7 @@ RESP_TYPE_V1 CDVRPTRV1Controller::getResponse(unsigned char *buffer, unsigned in offset += ret; if (ret == 0) - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); } length = buffer[1U] + buffer[2U] * 256U; @@ -779,7 +794,7 @@ RESP_TYPE_V1 CDVRPTRV1Controller::getResponse(unsigned char *buffer, unsigned in while (offset < length) { ret = m_serial.read(buffer + offset + DVRPTR_HEADER_LENGTH, length - offset); if (ret < 0) { - wxLogError(wxT("Error when reading from the DV-RPTR")); + wxLogError("Error when reading from the DV-RPTR"); return RT1_ERROR; } @@ -787,12 +802,12 @@ RESP_TYPE_V1 CDVRPTRV1Controller::getResponse(unsigned char *buffer, unsigned in offset += ret; if (ret == 0) - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); } length += DVRPTR_HEADER_LENGTH; - // CUtils::dump(wxT("Received"), buffer, length); + // CUtils::dump("Received", buffer, length); switch (type) { case DVRPTR_GET_STATUS: @@ -826,149 +841,100 @@ RESP_TYPE_V1 CDVRPTRV1Controller::getResponse(unsigned char *buffer, unsigned in } } -wxString CDVRPTRV1Controller::getPath() const +std::string CDVRPTRV1Controller::getPath() const { return m_path; } bool CDVRPTRV1Controller::findPort() { - if (m_path.IsEmpty()) + if (m_path.empty()) return false; -#if defined(__WINDOWS__) -#else - wxDir dir; - bool ret1 = dir.Open(wxT("/sys/class/tty")); - if (!ret1) { - wxLogError(wxT("Cannot open directory /sys/class/tty")); + DIR* dir = ::opendir("/sys/class/tty"); + if (dir == nullptr) { + wxLogError("Cannot open directory /sys/class/tty"); return false; } - wxString fileName; - ret1 = dir.GetFirst(&fileName, wxT("ttyACM*")); - while (ret1) { - wxString path; - path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str()); + struct dirent* entry; + while ((entry = ::readdir(dir)) != nullptr) { + std::string fileName(entry->d_name); - char cpath[255U]; - ::memset(cpath, 0x00U, 255U); + // Match ttyACM* entries + if (fileName.substr(0, 6) != "ttyACM") + continue; - for (unsigned int i = 0U; i < path.Len(); i++) - cpath[i] = path.GetChar(i); + std::string path = "/sys/class/tty/" + fileName; + + char cpath[255U]; + ::strncpy(cpath, path.c_str(), sizeof(cpath) - 1); + cpath[sizeof(cpath) - 1] = '\0'; char symlink[255U]; - int ret2 = ::readlink(cpath, symlink, 255U); + int ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret2 < 0) { - ::strcat(cpath, "/device"); - ret2 = ::readlink(cpath, symlink, 255U); + ::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1); + ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret2 < 0) { - wxLogError(wxT("Error from readlink()")); + wxLogError("Error from readlink()"); + ::closedir(dir); return false; } - - path = wxString(symlink, wxConvLocal, ret2); + symlink[ret2] = '\0'; + path = std::string(symlink, ret2); } else { + symlink[ret2] = '\0'; + std::string fullPath(symlink, ret2); // Get all but the last section - wxString fullPath = wxString(symlink, wxConvLocal, ret2); - path = fullPath.BeforeLast(wxT('/')); + size_t pos = fullPath.rfind('/'); + path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath; } - if (path.IsSameAs(m_path)) { - m_port.Printf(wxT("/dev/%s"), fileName.c_str()); + if (path == m_path) { + m_port = "/dev/" + fileName; - wxLogMessage(wxT("Found modem port of %s based on the path"), m_port.c_str()); + wxLogMessage("Found modem port of %s based on the path", m_port.c_str()); + ::closedir(dir); return true; } - - ret1 = dir.GetNext(&fileName); } -#endif + ::closedir(dir); return false; } bool CDVRPTRV1Controller::findPath() { -#if defined(__WINDOWS__) -#ifdef notdef - GUID guids[5U]; - - DWORD count; - BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count); - if (!res) { - wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError()); - return false; - } - - for (DWORD i = 0U; i < count; i++) { - HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); - if (devInfo == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); - return false; - } - - SP_DEVICE_INTERFACE_DATA devInfoData; - devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - - for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) { - // Find the required length of the device structure - DWORD length; - ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); - - PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); - detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - // Get the detailed data into the newly allocated device structure - DWORD required; - res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); - if (!res) { - wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - ::free(detailData); - } - - ::SetupDiDestroyDeviceInfoList(devInfo); - } - - return false; -#endif -#else - wxString path; - path.Printf(wxT("/sys/class/tty/%s"), m_port.Mid(5U).c_str()); + std::string path = "/sys/class/tty/" + m_port.substr(5U); char cpath[255U]; - ::memset(cpath, 0x00U, 255U); - - for (unsigned int i = 0U; i < path.Len(); i++) - cpath[i] = path.GetChar(i); + ::strncpy(cpath, path.c_str(), sizeof(cpath) - 1); + cpath[sizeof(cpath) - 1] = '\0'; char symlink[255U]; - int ret = ::readlink(cpath, symlink, 255U); + int ret = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret < 0) { - ::strcat(cpath, "/device"); - ret = ::readlink(cpath, symlink, 255U); + ::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1); + ret = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret < 0) { - wxLogError(wxT("Error from readlink()")); + wxLogError("Error from readlink()"); return false; } - - path = wxString(symlink, wxConvLocal, ret); + symlink[ret] = '\0'; + path = std::string(symlink, ret); } else { - wxString fullPath = wxString(symlink, wxConvLocal, ret); - path = fullPath.BeforeLast(wxT('/')); + symlink[ret] = '\0'; + std::string fullPath(symlink, ret); + size_t pos = fullPath.rfind('/'); + path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath; } - if (m_path.IsEmpty()) - wxLogMessage(wxT("Found modem path of %s"), path.c_str()); + if (m_path.empty()) + wxLogMessage("Found modem path of %s", path.c_str()); m_path = path; -#endif return true; } @@ -979,7 +945,7 @@ bool CDVRPTRV1Controller::findModem() // Tell the repeater that the signal has gone away if (m_rx) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; @@ -995,7 +961,7 @@ bool CDVRPTRV1Controller::findModem() while (!m_stopped) { count++; if (count >= 4U) { - wxLogMessage(wxT("Trying to reopen the modem")); + wxLogMessage("Trying to reopen the modem"); bool ret = findPort(); if (ret) { @@ -1007,7 +973,7 @@ bool CDVRPTRV1Controller::findModem() count = 0U; } - Sleep(500UL); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); } return false; @@ -1039,4 +1005,3 @@ bool CDVRPTRV1Controller::openModem() return true; } - diff --git a/Common/DVRPTRV1Controller.h b/Common/DVRPTRV1Controller.h index ef93af5..aa37c24 100644 --- a/Common/DVRPTRV1Controller.h +++ b/Common/DVRPTRV1Controller.h @@ -23,8 +23,9 @@ #include "RingBuffer.h" #include "Modem.h" #include "Utils.h" +#include "StdCompat.h" -#include +#include enum RESP_TYPE_V1 { RT1_TIMEOUT, @@ -45,13 +46,28 @@ enum RESP_TYPE_V1 { RT1_SET_TESTMDE }; +/* + * CDVRPTRV1Controller - Driver for DV-RPTR V1 boards via serial (USB CDC-ACM, 115200 baud). + * + * Uses the same DVRPTR binary framing protocol as CDVMegaController: + * [0xD0] [len_lo] [len_hi] [type] [txCounter] [pktCounter] [payload...] [cksum] + * + * Hardware-specific parameters passed to SET_CONFIG (physical layer): + * channel - selects which of the two physical input channels to use (bit 0x04 + * in the config byte). Some V1 boards have two discriminator inputs. + * modLevel - output modulation level, 0-100%, scaled to 0-255 in the wire frame. + * Sets the DAC drive level for the TX audio path. + * txDelay - PTT-to-data delay in milliseconds (little-endian uint16_t in frame). + * Allows time for the radio to reach full output power before + * transmitting the D-Star preamble. + * + * Reconnection and sysfs path tracking work identically to CDVMegaController. + */ class CDVRPTRV1Controller : public CModem { public: - CDVRPTRV1Controller(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay); + CDVRPTRV1Controller(const std::string& port, const std::string& path, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay); virtual ~CDVRPTRV1Controller(); - virtual void* Entry(); - virtual bool start(); virtual unsigned int getSpace(); @@ -60,11 +76,13 @@ public: virtual bool writeHeader(const CHeaderData& header); virtual bool writeData(const unsigned char* data, unsigned int length, bool end); - virtual wxString getPath() const; + virtual std::string getPath() const; private: - wxString m_port; - wxString m_path; + void entry(); + + std::string m_port; + std::string m_path; bool m_rxInvert; bool m_txInvert; bool m_channel; @@ -95,4 +113,3 @@ private: }; #endif - diff --git a/Common/DVRPTRV2Controller.cpp b/Common/DVRPTRV2Controller.cpp index 3a75117..82fafea 100644 --- a/Common/DVRPTRV2Controller.cpp +++ b/Common/DVRPTRV2Controller.cpp @@ -18,19 +18,23 @@ #include "DVRPTRV2Controller.h" #include "DStarDefines.h" +#include "Logger.h" #include "Timer.h" -#if defined(__WINDOWS__) -#include -#else -#include +#include +#include +#include +#include +#if !defined(_WIN32) +#include +#include #endif const unsigned int MAX_RESPONSES = 30U; const unsigned int BUFFER_LENGTH = 200U; -CDVRPTRV2Controller::CDVRPTRV2Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) : +CDVRPTRV2Controller::CDVRPTRV2Controller(const std::string& port, const std::string& path, bool txInvert, unsigned int modLevel, bool duplex, const std::string& callsign, unsigned int txDelay) : CModem(), m_connection(CT_USB), m_usbPort(port), @@ -42,20 +46,20 @@ m_modLevel(modLevel), m_duplex(duplex), m_callsign(callsign), m_txDelay(txDelay), -m_usb(NULL), -m_network(NULL), -m_buffer(NULL), +m_usb(nullptr), +m_network(nullptr), +m_buffer(nullptr), m_txData(1000U), m_rx(false) { - wxASSERT(!port.IsEmpty()); + assert(!port.empty()); m_usb = new CSerialDataController(port, SERIAL_115200); m_buffer = new unsigned char[BUFFER_LENGTH]; } -CDVRPTRV2Controller::CDVRPTRV2Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) : +CDVRPTRV2Controller::CDVRPTRV2Controller(const std::string& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const std::string& callsign, unsigned int txDelay) : CModem(), m_connection(CT_NETWORK), m_usbPort(), @@ -67,14 +71,14 @@ m_modLevel(modLevel), m_duplex(duplex), m_callsign(callsign), m_txDelay(txDelay), -m_usb(NULL), -m_network(NULL), -m_buffer(NULL), +m_usb(nullptr), +m_network(nullptr), +m_buffer(nullptr), m_txData(1000U), m_rx(false) { - wxASSERT(!address.IsEmpty()); - wxASSERT(port > 0U); + assert(!address.empty()); + assert(port > 0U); m_network = new CTCPReaderWriter(address, port); @@ -99,16 +103,14 @@ bool CDVRPTRV2Controller::start() findPath(); - Create(); - SetPriority(100U); - Run(); + m_thread = std::thread(&CDVRPTRV2Controller::entry, this); return true; } -void* CDVRPTRV2Controller::Entry() +void CDVRPTRV2Controller::entry() { - wxLogMessage(wxT("Starting DV-RPTR2 Modem Controller thread")); + wxLogMessage("Starting DV-RPTR2 Modem Controller thread"); // Clock every 5ms-ish CTimer pollTimer(200U, 0U, 250U); @@ -123,8 +125,8 @@ void* CDVRPTRV2Controller::Entry() if (!ret) { ret = findModem(); if (!ret) { - wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread")); - return NULL; + wxLogMessage("Stopping DV-RPTR2 Modem Controller thread"); + return; } } @@ -141,15 +143,15 @@ void* CDVRPTRV2Controller::Entry() case RT2_ERROR: { bool ret = findModem(); if (!ret) { - wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread")); - return NULL; + wxLogMessage("Stopping DV-RPTR2 Modem Controller thread"); + return; } } break; case RT2_HEADER: { - // CUtils::dump(wxT("RT2_HEADER"), m_buffer, length); - wxMutexLocker locker(m_mutex); + // CUtils::dump("RT2_HEADER", m_buffer, length); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_HEADER; @@ -174,8 +176,8 @@ void* CDVRPTRV2Controller::Entry() break; case RT2_DATA: { - // CUtils::dump(wxT("RT2_DATA"), m_buffer, length); - wxMutexLocker locker(m_mutex); + // CUtils::dump("RT2_DATA", m_buffer, length); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_DATA; @@ -200,7 +202,7 @@ void* CDVRPTRV2Controller::Entry() case RT2_SPACE: space = m_buffer[9U]; - // CUtils::dump(wxT("RT2_SPACE"), m_buffer, length); + // CUtils::dump("RT2_SPACE", m_buffer, length); break; // These should not be received in this loop, but don't complain if we do @@ -209,8 +211,8 @@ void* CDVRPTRV2Controller::Entry() break; default: - wxLogMessage(wxT("Unknown DV-RPTR2 message, type")); - CUtils::dump(wxT("Buffer dump"), m_buffer, length); + wxLogMessage("Unknown DV-RPTR2 message, type"); + CUtils::dump("Buffer dump", m_buffer, length); break; } @@ -220,20 +222,20 @@ void* CDVRPTRV2Controller::Entry() unsigned char data[200U]; { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); m_txData.getData(&len, 1U); m_txData.getData(data, len); } - // CUtils::dump(wxT("Write"), data, len); + // CUtils::dump("Write", data, len); bool ret = writeModem(data, len); if (!ret) { ret = findModem(); if (!ret) { - wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread")); - return NULL; + wxLogMessage("Stopping DV-RPTR2 Modem Controller thread"); + return; } } else { if (len > 100U) @@ -244,23 +246,21 @@ void* CDVRPTRV2Controller::Entry() } } - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); pollTimer.clock(); } - wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread")); + wxLogMessage("Stopping DV-RPTR2 Modem Controller thread"); closeModem(); - - return NULL; } bool CDVRPTRV2Controller::writeHeader(const CHeaderData& header) { bool ret = m_txData.hasSpace(106U); if (!ret) { - wxLogWarning(wxT("No space to write the header")); + wxLogWarning("No space to write the header"); return false; } @@ -284,27 +284,27 @@ bool CDVRPTRV2Controller::writeHeader(const CHeaderData& header) buffer[10U] = header.getFlag2(); buffer[11U] = header.getFlag3(); - wxString rpt2 = header.getRptCall2(); - for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 12U] = rpt2.GetChar(i); + std::string rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 12U] = rpt2[i]; - wxString rpt1 = header.getRptCall1(); - for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 20U] = rpt1.GetChar(i); + std::string rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 20U] = rpt1[i]; - wxString your = header.getYourCall(); - for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 28U] = your.GetChar(i); + std::string your = header.getYourCall(); + for (unsigned int i = 0U; i < your.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 28U] = your[i]; - wxString my1 = header.getMyCall1(); - for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 36U] = my1.GetChar(i); + std::string my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 36U] = my1[i]; - wxString my2 = header.getMyCall2(); - for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) - buffer[i + 44U] = my2.GetChar(i); + std::string my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.size() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 44U] = my2[i]; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char len = 105U; m_txData.addData(&len, 1U); @@ -320,7 +320,7 @@ bool CDVRPTRV2Controller::writeData(const unsigned char* data, unsigned int, boo { bool ret = m_txData.hasSpace(18U); if (!ret) { - wxLogWarning(wxT("No space to write data")); + wxLogWarning("No space to write data"); return false; } @@ -344,7 +344,7 @@ bool CDVRPTRV2Controller::writeData(const unsigned char* data, unsigned int, boo m_tx = false; } - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char len = 17U; m_txData.addData(&len, 1U); @@ -380,7 +380,7 @@ bool CDVRPTRV2Controller::readSerial() buffer[7U] = '0'; buffer[8U] = '0'; - // CUtils::dump(wxT("Written"), buffer, 105U); + // CUtils::dump("Written", buffer, 105U); bool ret = writeModem(buffer, 105U); if (!ret) @@ -390,20 +390,20 @@ bool CDVRPTRV2Controller::readSerial() unsigned int length; RESP_TYPE_V2 resp; do { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); resp = getResponse(m_buffer, length); if (resp != RT2_QUERY) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DV-RPTR modem is not responding to the query command")); + wxLogError("The DV-RPTR modem is not responding to the query command"); return false; } } } while (resp != RT2_QUERY); - wxLogInfo(wxT("DV-RPTR Modem Hardware serial: 0x%02X%02X%02x%02X"), m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U]); + wxLogInfo("DV-RPTR Modem Hardware serial: 0x%02X%02X%02x%02X", m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U]); return true; } @@ -423,7 +423,7 @@ bool CDVRPTRV2Controller::readSpace() buffer[8U] = '1'; buffer[9U] = 0x00U; - // CUtils::dump(wxT("Written"), buffer, 10U); + // CUtils::dump("Written", buffer, 10U); return writeModem(buffer, 10U); } @@ -445,8 +445,8 @@ bool CDVRPTRV2Controller::setConfig() buffer[8U] = '1'; ::memset(buffer + 9U, ' ', LONG_CALLSIGN_LENGTH); - for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH && i < m_callsign.Len(); i++) - buffer[9U + i] = m_callsign.GetChar(i); + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH && i < m_callsign.size(); i++) + buffer[9U + i] = m_callsign[i]; buffer[65U] = m_duplex ? 0x03U : 0x00U; @@ -466,7 +466,7 @@ bool CDVRPTRV2Controller::setConfig() buffer[89U] = m_txDelay / 10U; - // CUtils::dump(wxT("Written"), buffer, 105U); + // CUtils::dump("Written", buffer, 105U); bool ret = writeModem(buffer, 105U); if (!ret) @@ -476,34 +476,41 @@ bool CDVRPTRV2Controller::setConfig() unsigned int length; RESP_TYPE_V2 resp; do { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); resp = getResponse(m_buffer, length); if (resp != RT2_CONFIG) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DV-RPTR modem is not responding to the configure command")); + wxLogError("The DV-RPTR modem is not responding to the configure command"); return false; } } } while (resp != RT2_CONFIG); - // CUtils::dump(wxT("Response"), m_buffer, length); + // CUtils::dump("Response", m_buffer, length); - wxString firmware((char*)(m_buffer + 9U), wxConvLocal); + std::string firmware((char*)(m_buffer + 9U)); - wxLogInfo(wxT("DV-RPTR Modem Firmware version: %s"), firmware.c_str()); + wxLogInfo("DV-RPTR Modem Firmware version: %s", firmware.c_str()); return true; } +// Reads one "HEAD" protocol frame. +// Scans the byte stream for the 4-byte magic "HEAD". The 5th byte is a type +// character that determines the total frame length: +// 'X' -> 105 bytes (control/header frames, e.g. HEADX9000/9001/0002) +// 'Y' -> 10 bytes (status/space frames, e.g. HEADY9011) +// 'Z' -> 20 bytes (voice data frames, e.g. HEADZ) +// Sub-type is identified by bytes[5..8] (4 ASCII digits) for 'X' and 'Y' frames. RESP_TYPE_V2 CDVRPTRV2Controller::getResponse(unsigned char *buffer, unsigned int& length) { // Get the start of the frame or nothing at all int ret = readModem(buffer + 0U, 5U); if (ret < 0) { - wxLogError(wxT("Error when reading from the DV-RPTR")); + wxLogError("Error when reading from the DV-RPTR"); return RT2_ERROR; } @@ -518,7 +525,7 @@ RESP_TYPE_V2 CDVRPTRV2Controller::getResponse(unsigned char *buffer, unsigned in ret = readModem(buffer + 4U, 1U); if (ret < 0) { - wxLogError(wxT("Error when reading from the DV-RPTR")); + wxLogError("Error when reading from the DV-RPTR"); return RT2_ERROR; } @@ -537,7 +544,7 @@ RESP_TYPE_V2 CDVRPTRV2Controller::getResponse(unsigned char *buffer, unsigned in length = 20U; break; default: - wxLogError(wxT("DV-RPTR frame type is incorrect - 0x%02X"), buffer[4U]); + wxLogError("DV-RPTR frame type is incorrect - 0x%02X", buffer[4U]); return RT2_UNKNOWN; } @@ -546,7 +553,7 @@ RESP_TYPE_V2 CDVRPTRV2Controller::getResponse(unsigned char *buffer, unsigned in while (offset < length) { ret = readModem(buffer + offset, length - offset); if (ret < 0) { - wxLogError(wxT("Error when reading from the DV-RPTR")); + wxLogError("Error when reading from the DV-RPTR"); return RT2_ERROR; } @@ -554,10 +561,10 @@ RESP_TYPE_V2 CDVRPTRV2Controller::getResponse(unsigned char *buffer, unsigned in offset += ret; if (ret == 0) - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); } - // CUtils::dump(wxT("Received"), buffer, length); + // CUtils::dump("Received", buffer, length); if (::memcmp(buffer + 0U, "HEADZ", 5U) == 0) { return RT2_DATA; @@ -577,157 +584,115 @@ RESP_TYPE_V2 CDVRPTRV2Controller::getResponse(unsigned char *buffer, unsigned in return RT2_UNKNOWN; } -wxString CDVRPTRV2Controller::getPath() const +std::string CDVRPTRV2Controller::getPath() const { return m_usbPath; } bool CDVRPTRV2Controller::findPort() { +#if !defined(_WIN32) if (m_connection != CT_USB) return true; - if (m_usbPath.IsEmpty()) + if (m_usbPath.empty()) return false; -#if defined(__WINDOWS__) -#else - wxDir dir; - bool ret1 = dir.Open(wxT("/sys/class/tty")); - if (!ret1) { - wxLogError(wxT("Cannot open directory /sys/class/tty")); + DIR* dir = ::opendir("/sys/class/tty"); + if (dir == nullptr) { + wxLogError("Cannot open directory /sys/class/tty"); return false; } - wxString fileName; - ret1 = dir.GetFirst(&fileName, wxT("ttyACM*")); - while (ret1) { - wxString path; - path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str()); + struct dirent* entry; + while ((entry = ::readdir(dir)) != nullptr) { + std::string fileName(entry->d_name); - char cpath[255U]; - ::memset(cpath, 0x00U, 255U); + // Match ttyACM* entries + if (fileName.substr(0, 6) != "ttyACM") + continue; + + std::string path = "/sys/class/tty/" + fileName; - for (unsigned int i = 0U; i < path.Len(); i++) - cpath[i] = path.GetChar(i); + char cpath[255U]; + ::strncpy(cpath, path.c_str(), sizeof(cpath) - 1); + cpath[sizeof(cpath) - 1] = '\0'; char symlink[255U]; - int ret2 = ::readlink(cpath, symlink, 255U); + int ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret2 < 0) { - ::strcat(cpath, "/device"); - ret2 = ::readlink(cpath, symlink, 255U); + ::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1); + ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret2 < 0) { - wxLogError(wxT("Error from readlink()")); + wxLogError("Error from readlink()"); + ::closedir(dir); return false; } - - path = wxString(symlink, wxConvLocal, ret2); + symlink[ret2] = '\0'; + path = std::string(symlink, ret2); } else { - // Get all but the last section - wxString fullPath = wxString(symlink, wxConvLocal, ret2); - path = fullPath.BeforeLast(wxT('/')); + symlink[ret2] = '\0'; + std::string fullPath(symlink, ret2); + size_t pos = fullPath.rfind('/'); + path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath; } - if (path.IsSameAs(m_usbPath)) { - m_usbPort.Printf(wxT("/dev/%s"), fileName.c_str()); + if (path == m_usbPath) { + m_usbPort = "/dev/" + fileName; - wxLogMessage(wxT("Found modem port of %s based on the path"), m_usbPort.c_str()); + wxLogMessage("Found modem port of %s based on the path", m_usbPort.c_str()); + ::closedir(dir); return true; } - - ret1 = dir.GetNext(&fileName); } -#endif + ::closedir(dir); return false; +#else + return true; +#endif } bool CDVRPTRV2Controller::findPath() { +#if !defined(_WIN32) if (m_connection != CT_USB) return true; -#if defined(__WINDOWS__) -#ifdef notdef - GUID guids[5U]; - - DWORD count; - BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count); - if (!res) { - wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError()); - return false; - } - - for (DWORD i = 0U; i < count; i++) { - HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); - if (devInfo == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); - return false; - } - - SP_DEVICE_INTERFACE_DATA devInfoData; - devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - - for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) { - // Find the required length of the device structure - DWORD length; - ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); - - PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); - detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - // Get the detailed data into the newly allocated device structure - DWORD required; - res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); - if (!res) { - wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - ::free(detailData); - } - - ::SetupDiDestroyDeviceInfoList(devInfo); - } - - return false; -#endif -#else - wxString path; - path.Printf(wxT("/sys/class/tty/%s"), m_usbPort.Mid(5U).c_str()); + std::string path = "/sys/class/tty/" + m_usbPort.substr(5U); char cpath[255U]; - ::memset(cpath, 0x00U, 255U); - - for (unsigned int i = 0U; i < path.Len(); i++) - cpath[i] = path.GetChar(i); + ::strncpy(cpath, path.c_str(), sizeof(cpath) - 1); + cpath[sizeof(cpath) - 1] = '\0'; char symlink[255U]; - int ret = ::readlink(cpath, symlink, 255U); + int ret = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret < 0) { - ::strcat(cpath, "/device"); - ret = ::readlink(cpath, symlink, 255U); + ::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1); + ret = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret < 0) { - wxLogError(wxT("Error from readlink()")); + wxLogError("Error from readlink()"); return false; } - - path = wxString(symlink, wxConvLocal, ret); + symlink[ret] = '\0'; + path = std::string(symlink, ret); } else { - wxString fullPath = wxString(symlink, wxConvLocal, ret); - path = fullPath.BeforeLast(wxT('/')); + symlink[ret] = '\0'; + std::string fullPath(symlink, ret); + size_t pos = fullPath.rfind('/'); + path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath; } - if (m_usbPath.IsEmpty()) - wxLogMessage(wxT("Found modem path of %s"), path.c_str()); + if (m_usbPath.empty()) + wxLogMessage("Found modem path of %s", path.c_str()); m_usbPath = path; -#endif return true; +#else + return true; +#endif } bool CDVRPTRV2Controller::findModem() @@ -736,7 +701,7 @@ bool CDVRPTRV2Controller::findModem() // Tell the repeater that the signal has gone away if (m_rx) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; @@ -752,7 +717,7 @@ bool CDVRPTRV2Controller::findModem() while (!m_stopped) { count++; if (count >= 4U) { - wxLogMessage(wxT("Trying to reopen the modem")); + wxLogMessage("Trying to reopen the modem"); bool ret = findPort(); if (ret) { @@ -764,7 +729,7 @@ bool CDVRPTRV2Controller::findModem() count = 0U; } - Sleep(500UL); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); } return false; @@ -782,7 +747,7 @@ bool CDVRPTRV2Controller::openModem() ret = m_network->open(); break; default: - wxLogError(wxT("Invalid connection type: %d"), int(m_connection)); + wxLogError("Invalid connection type: %d", int(m_connection)); break; } diff --git a/Common/DVRPTRV2Controller.h b/Common/DVRPTRV2Controller.h index 8c35f7d..6c27e47 100644 --- a/Common/DVRPTRV2Controller.h +++ b/Common/DVRPTRV2Controller.h @@ -25,8 +25,9 @@ #include "RingBuffer.h" #include "Modem.h" #include "Utils.h" +#include "StdCompat.h" -#include +#include enum RESP_TYPE_V2 { RT2_TIMEOUT, @@ -39,14 +40,43 @@ enum RESP_TYPE_V2 { RT2_DATA }; +/* + * CDVRPTRV2Controller - Driver for DV-RPTR V2 boards. + * + * Supports two physical connection types, selected by constructor: + * CT_USB - USB CDC-ACM serial port (same as V1). + * CT_NETWORK - TCP socket (address + port); used for networked / remote modems. + * + * The CONNECTION_TYPE flag (m_connection) controls which of the two backing + * objects (m_usb / m_network) is used; readModem(), writeModem(), closeModem() + * dispatch through it uniformly so the rest of the logic is connection-agnostic. + * + * Protocol: ASCII-framed "HEAD" protocol (distinct from the binary DVRPTR protocol + * used by V1). Each frame starts with the 4-byte magic "HEAD" followed by a + * 1-byte type character: + * 'X' - 105-byte control/header frame (HEADX) + * 'Y' - 10-byte space/status frame (HEADY) + * 'Z' - 20-byte voice data frame (HEADZ) + * Frame subtypes are identified by 4 ASCII digits at bytes [5..8]. + * + * On startup the driver sends a HEADX/9000 query to get the hardware serial, + * then HEADX/9001 to configure the modem (callsign, duplex, modLevel, txDelay). + * During operation the driver polls HEADY/9011 every 250ms to get TX buffer space. + * + * The header frame (HEADX/0002) also contains the first DV frame of the + * transmission (at byte offset 51), so the repeater receives both the D-Star + * header and the first voice frame in a single network packet. + * + * Reconnection works identically to V1: findPort() -> openModem() retried every 2s. + */ class CDVRPTRV2Controller : public CModem { public: - CDVRPTRV2Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay); - CDVRPTRV2Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay); + // USB serial connection. + CDVRPTRV2Controller(const std::string& port, const std::string& path, bool txInvert, unsigned int modLevel, bool duplex, const std::string& callsign, unsigned int txDelay); + // TCP network connection. + CDVRPTRV2Controller(const std::string& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const std::string& callsign, unsigned int txDelay); virtual ~CDVRPTRV2Controller(); - virtual void* Entry(); - virtual bool start(); virtual unsigned int getSpace(); @@ -55,18 +85,20 @@ public: virtual bool writeHeader(const CHeaderData& header); virtual bool writeData(const unsigned char* data, unsigned int length, bool end); - virtual wxString getPath() const; + virtual std::string getPath() const; private: + void entry(); + CONNECTION_TYPE m_connection; - wxString m_usbPort; - wxString m_usbPath; - wxString m_address; + std::string m_usbPort; + std::string m_usbPath; + std::string m_address; unsigned int m_port; bool m_txInvert; unsigned int m_modLevel; bool m_duplex; - wxString m_callsign; + std::string m_callsign; unsigned int m_txDelay; CSerialDataController* m_usb; CTCPReaderWriter* m_network; diff --git a/Common/DVRPTRV3Controller.cpp b/Common/DVRPTRV3Controller.cpp index 79fbf09..4d3dafa 100644 --- a/Common/DVRPTRV3Controller.cpp +++ b/Common/DVRPTRV3Controller.cpp @@ -18,19 +18,23 @@ #include "DVRPTRV3Controller.h" #include "DStarDefines.h" +#include "Logger.h" #include "Timer.h" -#if defined(__WINDOWS__) -#include -#else -#include +#include +#include +#include +#include +#if !defined(_WIN32) +#include +#include #endif const unsigned int MAX_RESPONSES = 30U; const unsigned int BUFFER_LENGTH = 200U; -CDVRPTRV3Controller::CDVRPTRV3Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) : +CDVRPTRV3Controller::CDVRPTRV3Controller(const std::string& port, const std::string& path, bool txInvert, unsigned int modLevel, bool duplex, const std::string& callsign, unsigned int txDelay) : CModem(), m_connection(CT_USB), m_usbPort(port), @@ -42,20 +46,20 @@ m_modLevel(modLevel), m_duplex(duplex), m_callsign(callsign), m_txDelay(txDelay), -m_usb(NULL), -m_network(NULL), -m_buffer(NULL), +m_usb(nullptr), +m_network(nullptr), +m_buffer(nullptr), m_txData(1000U), m_rx(false) { - wxASSERT(!port.IsEmpty()); + assert(!port.empty()); m_usb = new CSerialDataController(port, SERIAL_115200); m_buffer = new unsigned char[BUFFER_LENGTH]; } -CDVRPTRV3Controller::CDVRPTRV3Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) : +CDVRPTRV3Controller::CDVRPTRV3Controller(const std::string& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const std::string& callsign, unsigned int txDelay) : CModem(), m_connection(CT_NETWORK), m_usbPort(), @@ -67,14 +71,14 @@ m_modLevel(modLevel), m_duplex(duplex), m_callsign(callsign), m_txDelay(txDelay), -m_usb(NULL), -m_network(NULL), -m_buffer(NULL), +m_usb(nullptr), +m_network(nullptr), +m_buffer(nullptr), m_txData(1000U), m_rx(false) { - wxASSERT(!address.IsEmpty()); - wxASSERT(port > 0U); + assert(!address.empty()); + assert(port > 0U); m_network = new CTCPReaderWriter(address, port); @@ -99,16 +103,14 @@ bool CDVRPTRV3Controller::start() findPath(); - Create(); - SetPriority(100U); - Run(); + m_thread = std::thread(&CDVRPTRV3Controller::entry, this); return true; } -void* CDVRPTRV3Controller::Entry() +void CDVRPTRV3Controller::entry() { - wxLogMessage(wxT("Starting DV-RPTR3 Modem Controller thread")); + wxLogMessage("Starting DV-RPTR3 Modem Controller thread"); // Clock every 5ms-ish CTimer pollTimer(200U, 0U, 250U); @@ -123,8 +125,8 @@ void* CDVRPTRV3Controller::Entry() if (!ret) { ret = findModem(); if (!ret) { - wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread")); - return NULL; + wxLogMessage("Stopping DV-RPTR3 Modem Controller thread"); + return; } } @@ -141,15 +143,15 @@ void* CDVRPTRV3Controller::Entry() case RT3_ERROR: { bool ret = findModem(); if (!ret) { - wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread")); - return NULL; + wxLogMessage("Stopping DV-RPTR3 Modem Controller thread"); + return; } } break; case RT3_HEADER: { - // CUtils::dump(wxT("RT3_HEADER"), m_buffer, length); - wxMutexLocker locker(m_mutex); + // CUtils::dump("RT3_HEADER", m_buffer, length); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_HEADER; @@ -174,8 +176,8 @@ void* CDVRPTRV3Controller::Entry() break; case RT3_DATA: { - // CUtils::dump(wxT("RT3_DATA"), m_buffer, length); - wxMutexLocker locker(m_mutex); + // CUtils::dump("RT3_DATA", m_buffer, length); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_DATA; @@ -200,7 +202,7 @@ void* CDVRPTRV3Controller::Entry() case RT3_SPACE: space = m_buffer[9U]; - // CUtils::dump(wxT("RT3_SPACE"), m_buffer, length); + // CUtils::dump("RT3_SPACE", m_buffer, length); break; // These should not be received in this loop, but don't complain if we do @@ -209,8 +211,8 @@ void* CDVRPTRV3Controller::Entry() break; default: - wxLogMessage(wxT("Unknown DV-RPTR3 message, type")); - CUtils::dump(wxT("Buffer dump"), m_buffer, length); + wxLogMessage("Unknown DV-RPTR3 message, type"); + CUtils::dump("Buffer dump", m_buffer, length); break; } @@ -220,20 +222,20 @@ void* CDVRPTRV3Controller::Entry() unsigned char data[200U]; { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); m_txData.getData(&len, 1U); m_txData.getData(data, len); } - // CUtils::dump(wxT("Write"), data, len); + // CUtils::dump("Write", data, len); bool ret = writeModem(data, len); if (!ret) { ret = findModem(); if (!ret) { - wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread")); - return NULL; + wxLogMessage("Stopping DV-RPTR3 Modem Controller thread"); + return; } } else { if (len > 100U) @@ -244,23 +246,21 @@ void* CDVRPTRV3Controller::Entry() } } - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); pollTimer.clock(); } - wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread")); + wxLogMessage("Stopping DV-RPTR3 Modem Controller thread"); closeModem(); - - return NULL; } bool CDVRPTRV3Controller::writeHeader(const CHeaderData& header) { bool ret = m_txData.hasSpace(106U); if (!ret) { - wxLogWarning(wxT("No space to write the header")); + wxLogWarning("No space to write the header"); return false; } @@ -284,27 +284,27 @@ bool CDVRPTRV3Controller::writeHeader(const CHeaderData& header) buffer[10U] = header.getFlag2(); buffer[11U] = header.getFlag3(); - wxString rpt2 = header.getRptCall2(); - for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 12U] = rpt2.GetChar(i); + std::string rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 12U] = rpt2[i]; - wxString rpt1 = header.getRptCall1(); - for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 20U] = rpt1.GetChar(i); + std::string rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 20U] = rpt1[i]; - wxString your = header.getYourCall(); - for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 28U] = your.GetChar(i); + std::string your = header.getYourCall(); + for (unsigned int i = 0U; i < your.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 28U] = your[i]; - wxString my1 = header.getMyCall1(); - for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 36U] = my1.GetChar(i); + std::string my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 36U] = my1[i]; - wxString my2 = header.getMyCall2(); - for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) - buffer[i + 44U] = my2.GetChar(i); + std::string my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.size() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 44U] = my2[i]; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char len = 105U; m_txData.addData(&len, 1U); @@ -320,7 +320,7 @@ bool CDVRPTRV3Controller::writeData(const unsigned char* data, unsigned int, boo { bool ret = m_txData.hasSpace(18U); if (!ret) { - wxLogWarning(wxT("No space to write data")); + wxLogWarning("No space to write data"); return false; } @@ -344,7 +344,7 @@ bool CDVRPTRV3Controller::writeData(const unsigned char* data, unsigned int, boo m_tx = false; } - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char len = 17U; m_txData.addData(&len, 1U); @@ -380,7 +380,7 @@ bool CDVRPTRV3Controller::readSerial() buffer[7U] = '0'; buffer[8U] = '0'; - // CUtils::dump(wxT("Written"), buffer, 105U); + // CUtils::dump("Written", buffer, 105U); bool ret = writeModem(buffer, 105U); if (!ret) @@ -390,20 +390,20 @@ bool CDVRPTRV3Controller::readSerial() unsigned int length; RESP_TYPE_V3 resp; do { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); resp = getResponse(m_buffer, length); if (resp != RT3_QUERY) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DV-RPTR modem is not responding to the query command")); + wxLogError("The DV-RPTR modem is not responding to the query command"); return false; } } } while (resp != RT3_QUERY); - wxLogInfo(wxT("DV-RPTR Modem Hardware serial: 0x%02X%02X%02x%02X"), m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U]); + wxLogInfo("DV-RPTR Modem Hardware serial: 0x%02X%02X%02x%02X", m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U]); return true; } @@ -423,7 +423,7 @@ bool CDVRPTRV3Controller::readSpace() buffer[8U] = '1'; buffer[9U] = 0x00U; - // CUtils::dump(wxT("Written"), buffer, 10U); + // CUtils::dump("Written", buffer, 10U); return writeModem(buffer, 10U); } @@ -445,8 +445,8 @@ bool CDVRPTRV3Controller::setConfig() buffer[8U] = '1'; ::memset(buffer + 9U, ' ', LONG_CALLSIGN_LENGTH); - for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH && i < m_callsign.Len(); i++) - buffer[9U + i] = m_callsign.GetChar(i); + for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH && i < m_callsign.size(); i++) + buffer[9U + i] = m_callsign[i]; buffer[65U] = m_duplex ? 0x03U : 0x00U; @@ -466,7 +466,7 @@ bool CDVRPTRV3Controller::setConfig() buffer[89U] = m_txDelay / 10U; - // CUtils::dump(wxT("Written"), buffer, 105U); + // CUtils::dump("Written", buffer, 105U); bool ret = writeModem(buffer, 105U); if (!ret) @@ -476,34 +476,36 @@ bool CDVRPTRV3Controller::setConfig() unsigned int length; RESP_TYPE_V3 resp; do { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); resp = getResponse(m_buffer, length); if (resp != RT3_CONFIG) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The DV-RPTR modem is not responding to the configure command")); + wxLogError("The DV-RPTR modem is not responding to the configure command"); return false; } } } while (resp != RT3_CONFIG); - // CUtils::dump(wxT("Response"), m_buffer, length); + // CUtils::dump("Response", m_buffer, length); - wxString firmware((char*)(m_buffer + 9U), wxConvLocal); + std::string firmware((char*)(m_buffer + 9U)); - wxLogInfo(wxT("DV-RPTR Modem Firmware version: %s"), firmware.c_str()); + wxLogInfo("DV-RPTR Modem Firmware version: %s", firmware.c_str()); return true; } +// Reads one "HEAD" protocol frame. See CDVRPTRV2Controller::getResponse() +// for full protocol documentation; V3 uses an identical frame structure. RESP_TYPE_V3 CDVRPTRV3Controller::getResponse(unsigned char *buffer, unsigned int& length) { // Get the start of the frame or nothing at all int ret = readModem(buffer + 0U, 5U); if (ret < 0) { - wxLogError(wxT("Error when reading from the DV-RPTR")); + wxLogError("Error when reading from the DV-RPTR"); return RT3_ERROR; } @@ -518,7 +520,7 @@ RESP_TYPE_V3 CDVRPTRV3Controller::getResponse(unsigned char *buffer, unsigned in ret = readModem(buffer + 4U, 1U); if (ret < 0) { - wxLogError(wxT("Error when reading from the DV-RPTR")); + wxLogError("Error when reading from the DV-RPTR"); return RT3_ERROR; } @@ -537,7 +539,7 @@ RESP_TYPE_V3 CDVRPTRV3Controller::getResponse(unsigned char *buffer, unsigned in length = 20U; break; default: - wxLogError(wxT("DV-RPTR frame type is incorrect - 0x%02X"), buffer[4U]); + wxLogError("DV-RPTR frame type is incorrect - 0x%02X", buffer[4U]); return RT3_UNKNOWN; } @@ -546,7 +548,7 @@ RESP_TYPE_V3 CDVRPTRV3Controller::getResponse(unsigned char *buffer, unsigned in while (offset < length) { ret = readModem(buffer + offset, length - offset); if (ret < 0) { - wxLogError(wxT("Error when reading from the DV-RPTR")); + wxLogError("Error when reading from the DV-RPTR"); return RT3_ERROR; } @@ -554,10 +556,10 @@ RESP_TYPE_V3 CDVRPTRV3Controller::getResponse(unsigned char *buffer, unsigned in offset += ret; if (ret == 0) - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); } - // CUtils::dump(wxT("Received"), buffer, length); + // CUtils::dump("Received", buffer, length); if (::memcmp(buffer + 0U, "HEADZ", 5U) == 0) { return RT3_DATA; @@ -577,157 +579,115 @@ RESP_TYPE_V3 CDVRPTRV3Controller::getResponse(unsigned char *buffer, unsigned in return RT3_UNKNOWN; } -wxString CDVRPTRV3Controller::getPath() const +std::string CDVRPTRV3Controller::getPath() const { return m_usbPath; } bool CDVRPTRV3Controller::findPort() { +#if !defined(_WIN32) if (m_connection != CT_USB) return true; - if (m_usbPath.IsEmpty()) + if (m_usbPath.empty()) return false; -#if defined(__WINDOWS__) -#else - wxDir dir; - bool ret1 = dir.Open(wxT("/sys/class/tty")); - if (!ret1) { - wxLogError(wxT("Cannot open directory /sys/class/tty")); + DIR* dir = ::opendir("/sys/class/tty"); + if (dir == nullptr) { + wxLogError("Cannot open directory /sys/class/tty"); return false; } - wxString fileName; - ret1 = dir.GetFirst(&fileName, wxT("ttyACM*")); - while (ret1) { - wxString path; - path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str()); + struct dirent* entry; + while ((entry = ::readdir(dir)) != nullptr) { + std::string fileName(entry->d_name); - char cpath[255U]; - ::memset(cpath, 0x00U, 255U); + // Match ttyACM* entries + if (fileName.substr(0, 6) != "ttyACM") + continue; + + std::string path = "/sys/class/tty/" + fileName; - for (unsigned int i = 0U; i < path.Len(); i++) - cpath[i] = path.GetChar(i); + char cpath[255U]; + ::strncpy(cpath, path.c_str(), sizeof(cpath) - 1); + cpath[sizeof(cpath) - 1] = '\0'; char symlink[255U]; - int ret2 = ::readlink(cpath, symlink, 255U); + int ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret2 < 0) { - ::strcat(cpath, "/device"); - ret2 = ::readlink(cpath, symlink, 255U); + ::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1); + ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret2 < 0) { - wxLogError(wxT("Error from readlink()")); + wxLogError("Error from readlink()"); + ::closedir(dir); return false; } - - path = wxString(symlink, wxConvLocal, ret2); + symlink[ret2] = '\0'; + path = std::string(symlink, ret2); } else { - // Get all but the last section - wxString fullPath = wxString(symlink, wxConvLocal, ret2); - path = fullPath.BeforeLast(wxT('/')); + symlink[ret2] = '\0'; + std::string fullPath(symlink, ret2); + size_t pos = fullPath.rfind('/'); + path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath; } - if (path.IsSameAs(m_usbPath)) { - m_usbPort.Printf(wxT("/dev/%s"), fileName.c_str()); + if (path == m_usbPath) { + m_usbPort = "/dev/" + fileName; - wxLogMessage(wxT("Found modem port of %s based on the path"), m_usbPort.c_str()); + wxLogMessage("Found modem port of %s based on the path", m_usbPort.c_str()); + ::closedir(dir); return true; } - - ret1 = dir.GetNext(&fileName); } -#endif + ::closedir(dir); return false; +#else + return true; +#endif } bool CDVRPTRV3Controller::findPath() { +#if !defined(_WIN32) if (m_connection != CT_USB) return true; -#if defined(__WINDOWS__) -#ifdef notdef - GUID guids[5U]; - - DWORD count; - BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count); - if (!res) { - wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError()); - return false; - } - - for (DWORD i = 0U; i < count; i++) { - HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); - if (devInfo == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); - return false; - } - - SP_DEVICE_INTERFACE_DATA devInfoData; - devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - - for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) { - // Find the required length of the device structure - DWORD length; - ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); - - PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); - detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - // Get the detailed data into the newly allocated device structure - DWORD required; - res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); - if (!res) { - wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - ::free(detailData); - } - - ::SetupDiDestroyDeviceInfoList(devInfo); - } - - return false; -#endif -#else - wxString path; - path.Printf(wxT("/sys/class/tty/%s"), m_usbPort.Mid(5U).c_str()); + std::string path = "/sys/class/tty/" + m_usbPort.substr(5U); char cpath[255U]; - ::memset(cpath, 0x00U, 255U); - - for (unsigned int i = 0U; i < path.Len(); i++) - cpath[i] = path.GetChar(i); + ::strncpy(cpath, path.c_str(), sizeof(cpath) - 1); + cpath[sizeof(cpath) - 1] = '\0'; char symlink[255U]; - int ret = ::readlink(cpath, symlink, 255U); + int ret = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret < 0) { - ::strcat(cpath, "/device"); - ret = ::readlink(cpath, symlink, 255U); + ::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1); + ret = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret < 0) { - wxLogError(wxT("Error from readlink()")); + wxLogError("Error from readlink()"); return false; } - - path = wxString(symlink, wxConvLocal, ret); + symlink[ret] = '\0'; + path = std::string(symlink, ret); } else { - wxString fullPath = wxString(symlink, wxConvLocal, ret); - path = fullPath.BeforeLast(wxT('/')); + symlink[ret] = '\0'; + std::string fullPath(symlink, ret); + size_t pos = fullPath.rfind('/'); + path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath; } - if (m_usbPath.IsEmpty()) - wxLogMessage(wxT("Found modem path of %s"), path.c_str()); + if (m_usbPath.empty()) + wxLogMessage("Found modem path of %s", path.c_str()); m_usbPath = path; -#endif return true; +#else + return true; +#endif } bool CDVRPTRV3Controller::findModem() @@ -736,7 +696,7 @@ bool CDVRPTRV3Controller::findModem() // Tell the repeater that the signal has gone away if (m_rx) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; @@ -752,7 +712,7 @@ bool CDVRPTRV3Controller::findModem() while (!m_stopped) { count++; if (count >= 4U) { - wxLogMessage(wxT("Trying to reopen the modem")); + wxLogMessage("Trying to reopen the modem"); bool ret = findPort(); if (ret) { @@ -764,7 +724,7 @@ bool CDVRPTRV3Controller::findModem() count = 0U; } - Sleep(500UL); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); } return false; @@ -782,7 +742,7 @@ bool CDVRPTRV3Controller::openModem() ret = m_network->open(); break; default: - wxLogError(wxT("Invalid connection type: %d"), int(m_connection)); + wxLogError("Invalid connection type: %d", int(m_connection)); break; } diff --git a/Common/DVRPTRV3Controller.h b/Common/DVRPTRV3Controller.h index 4829c61..dd8f513 100644 --- a/Common/DVRPTRV3Controller.h +++ b/Common/DVRPTRV3Controller.h @@ -25,8 +25,9 @@ #include "RingBuffer.h" #include "Modem.h" #include "Utils.h" +#include "StdCompat.h" -#include +#include enum RESP_TYPE_V3 { RT3_TIMEOUT, @@ -39,14 +40,22 @@ enum RESP_TYPE_V3 { RT3_DATA }; +/* + * CDVRPTRV3Controller - Driver for DV-RPTR V3 boards. + * + * Functionally identical to CDVRPTRV2Controller; V3 uses the same ASCII "HEAD" + * protocol and the same CT_USB / CT_NETWORK connection type switching. + * The code is a parallel implementation maintained separately for V3 firmware + * compatibility. See CDVRPTRV2Controller for full protocol documentation. + */ class CDVRPTRV3Controller : public CModem { public: - CDVRPTRV3Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay); - CDVRPTRV3Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay); + // USB serial connection. + CDVRPTRV3Controller(const std::string& port, const std::string& path, bool txInvert, unsigned int modLevel, bool duplex, const std::string& callsign, unsigned int txDelay); + // TCP network connection. + CDVRPTRV3Controller(const std::string& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const std::string& callsign, unsigned int txDelay); virtual ~CDVRPTRV3Controller(); - virtual void* Entry(); - virtual bool start(); virtual unsigned int getSpace(); @@ -55,18 +64,20 @@ public: virtual bool writeHeader(const CHeaderData& header); virtual bool writeData(const unsigned char* data, unsigned int length, bool end); - virtual wxString getPath() const; + virtual std::string getPath() const; private: + void entry(); + CONNECTION_TYPE m_connection; - wxString m_usbPort; - wxString m_usbPath; - wxString m_address; + std::string m_usbPort; + std::string m_usbPath; + std::string m_address; unsigned int m_port; bool m_txInvert; unsigned int m_modLevel; bool m_duplex; - wxString m_callsign; + std::string m_callsign; unsigned int m_txDelay; CSerialDataController* m_usb; CTCPReaderWriter* m_network; diff --git a/Common/DVTOOLFileReader.cpp b/Common/DVTOOLFileReader.cpp index 0a2550b..5bcf32b 100644 --- a/Common/DVTOOLFileReader.cpp +++ b/Common/DVTOOLFileReader.cpp @@ -20,7 +20,10 @@ #include "DStarDefines.h" #include "Utils.h" -#include +#include +#include +#include +#include "EndianCompat.h" static const char DVTOOL_SIGNATURE[] = "DVTOOL"; static const unsigned int DVTOOL_SIGNATURE_LENGTH = 6U; @@ -41,10 +44,10 @@ const unsigned int BUFFER_LENGTH = 255U; CDVTOOLFileReader::CDVTOOLFileReader() : m_fileName(), -m_file(), +m_file(nullptr), m_records(0U), m_type(DVTFR_NONE), -m_buffer(NULL), +m_buffer(nullptr), m_length(0U), m_end(false) { @@ -56,7 +59,7 @@ CDVTOOLFileReader::~CDVTOOLFileReader() delete[] m_buffer; } -wxString CDVTOOLFileReader::getFileName() const +std::string CDVTOOLFileReader::getFileName() const { return m_fileName; } @@ -66,34 +69,38 @@ unsigned int CDVTOOLFileReader::getRecords() const return m_records; } -bool CDVTOOLFileReader::open(const wxString& fileName) +bool CDVTOOLFileReader::open(const std::string& fileName) { m_fileName = fileName; - bool res = m_file.Open(fileName, wxT("rb")); - if (!res) + m_file = ::fopen(fileName.c_str(), "rb"); + if (m_file == nullptr) return false; unsigned char buffer[DVTOOL_SIGNATURE_LENGTH]; - size_t n = m_file.Read(buffer, DVTOOL_SIGNATURE_LENGTH); + size_t n = ::fread(buffer, 1U, DVTOOL_SIGNATURE_LENGTH, m_file); if (n != DVTOOL_SIGNATURE_LENGTH) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } if (::memcmp(buffer, DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH) != 0) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } - wxUint32 uint32; - n = m_file.Read(&uint32, sizeof(wxUint32)); - if (n != sizeof(wxUint32)) { - m_file.Close(); + uint32_t uint32; + n = ::fread(&uint32, 1U, sizeof(uint32_t), m_file); + if (n != sizeof(uint32_t)) { + ::fclose(m_file); + m_file = nullptr; return false; } - m_records = wxUINT32_SWAP_ON_LE(uint32); + // wxUINT32_SWAP_ON_LE swaps on little-endian to produce big-endian value + m_records = be32toh(uint32); m_end = false; return true; @@ -101,15 +108,16 @@ bool CDVTOOLFileReader::open(const wxString& fileName) DVTFR_TYPE CDVTOOLFileReader::read() { - wxUint16 uint16; - size_t n = m_file.Read(&uint16, sizeof(wxUint16)); - if (n != sizeof(wxUint16)) + uint16_t uint16; + size_t n = ::fread(&uint16, 1U, sizeof(uint16_t), m_file); + if (n != sizeof(uint16_t)) return DVTFR_NONE; - m_length = wxUINT16_SWAP_ON_BE(uint16) - 15U; + // wxUINT16_SWAP_ON_BE swaps on big-endian to get little-endian value + m_length = le16toh(uint16) - 15U; unsigned char bytes[FIXED_DATA_LENGTH]; - n = m_file.Read(bytes, DSVT_SIGNATURE_LENGTH); + n = ::fread(bytes, 1U, DSVT_SIGNATURE_LENGTH, m_file); if (n != DSVT_SIGNATURE_LENGTH) return DVTFR_NONE; @@ -117,17 +125,17 @@ DVTFR_TYPE CDVTOOLFileReader::read() return DVTFR_NONE; char flag; - n = m_file.Read(&flag, 1U); + n = ::fread(&flag, 1U, 1U, m_file); if (n != 1U) return DVTFR_NONE; m_type = (flag == HEADER_FLAG) ? DVTFR_HEADER : DVTFR_DATA; - n = m_file.Read(bytes, FIXED_DATA_LENGTH); + n = ::fread(bytes, 1U, FIXED_DATA_LENGTH, m_file); if (n != FIXED_DATA_LENGTH) return DVTFR_NONE; - n = m_file.Read(&flag, 1U); + n = ::fread(&flag, 1U, 1U, m_file); if (n != 1U) return DVTFR_NONE; @@ -136,7 +144,7 @@ DVTFR_TYPE CDVTOOLFileReader::read() m_end = true; } - n = m_file.Read(m_buffer, m_length); + n = ::fread(m_buffer, 1U, m_length, m_file); if (n != m_length) return DVTFR_NONE; @@ -146,7 +154,7 @@ DVTFR_TYPE CDVTOOLFileReader::read() CHeaderData* CDVTOOLFileReader::readHeader() { if (m_type != DVTFR_HEADER) - return NULL; + return nullptr; if (m_buffer[39U] == 0xFFU && m_buffer[40U] == 0xFFU) return new CHeaderData(m_buffer, RADIO_HEADER_LENGTH_BYTES, false); @@ -155,9 +163,9 @@ CHeaderData* CDVTOOLFileReader::readHeader() CHeaderData* header = new CHeaderData(m_buffer, RADIO_HEADER_LENGTH_BYTES, true); if (!header->isValid()) { - CUtils::dump(wxT("Header checksum failure"), m_buffer, RADIO_HEADER_LENGTH_BYTES); + CUtils::dump("Header checksum failure", m_buffer, RADIO_HEADER_LENGTH_BYTES); delete header; - return NULL; + return nullptr; } return header; @@ -165,8 +173,8 @@ CHeaderData* CDVTOOLFileReader::readHeader() unsigned int CDVTOOLFileReader::readData(unsigned char* buffer, unsigned int length, bool& end) { - wxASSERT(buffer != NULL); - wxASSERT(length > 0U); + assert(buffer != nullptr); + assert(length > 0U); if (m_type != DVTFR_DATA) return 0U; @@ -183,5 +191,8 @@ unsigned int CDVTOOLFileReader::readData(unsigned char* buffer, unsigned int len void CDVTOOLFileReader::close() { - m_file.Close(); + if (m_file != nullptr) { + ::fclose(m_file); + m_file = nullptr; + } } diff --git a/Common/DVTOOLFileReader.h b/Common/DVTOOLFileReader.h index 13ba0f1..1d15759 100644 --- a/Common/DVTOOLFileReader.h +++ b/Common/DVTOOLFileReader.h @@ -21,37 +21,62 @@ #include "HeaderData.h" +/* + * .dvtool file format (compatible with the DVTool PC application): + * + * [0..5] "DVTOOL" — 6-byte magic signature + * [6..9] record count (big-endian) — total number of DSVT records + * + * Each record: + * [0..1] total record length (little-endian, includes 15-byte fixed header) + * [2..5] "DSVT" — record signature + * [6] 0x10 = header frame, 0x20 = data/trailer frame + * [7..15] fixed 9-byte field (stream flags, sequence info) + * [16] flags: bit 7 = header record, bit 6 = trailer (end-of-stream) + * [17..] payload (radio header bytes, or DV frame data) + */ + enum DVTFR_TYPE { - DVTFR_NONE, - DVTFR_HEADER, - DVTFR_DATA + DVTFR_NONE, // No record read yet, or end of file. + DVTFR_HEADER, // Current record contains a radio header. + DVTFR_DATA // Current record contains a DV voice/data frame. }; -#include -#include +#include "StdCompat.h" +#include +/* + * Sequential reader for .dvtool recording files. + * + * Call open() to validate the signature and read the record count, then loop + * calling read() to advance to each record, followed by readHeader() or + * readData() to extract the payload. close() releases the file handle. + */ class CDVTOOLFileReader { public: CDVTOOLFileReader(); ~CDVTOOLFileReader(); - wxString getFileName() const; + std::string getFileName() const; unsigned int getRecords() const; - bool open(const wxString& fileName); + bool open(const std::string& fileName); + // Advances to the next record; returns its type or DVTFR_NONE at EOF. DVTFR_TYPE read(); + // Returns a newly allocated CHeaderData; caller takes ownership. CHeaderData* readHeader(); + // Copies DV frame payload into buffer; sets end=true on the trailer record. unsigned int readData(unsigned char* buffer, unsigned int length, bool& end); void close(); private: - wxString m_fileName; - wxFFile m_file; - wxUint32 m_records; + std::string m_fileName; + FILE* m_file; + uint32_t m_records; // Total record count from the file header. DVTFR_TYPE m_type; unsigned char* m_buffer; - unsigned int m_length; - bool m_end; + unsigned int m_length; // Payload length of the current record. + bool m_end; // True if the trailer bit was set in the current record. }; #endif diff --git a/Common/DVTOOLFileWriter.cpp b/Common/DVTOOLFileWriter.cpp index f642e35..9f570c5 100644 --- a/Common/DVTOOLFileWriter.cpp +++ b/Common/DVTOOLFileWriter.cpp @@ -20,8 +20,11 @@ #include "DVTOOLFileWriter.h" #include "DStarDefines.h" -#include -#include +#include +#include +#include +#include +#include "EndianCompat.h" static const char DVTOOL_SIGNATURE[] = "DVTOOL"; static unsigned int DVTOOL_SIGNATURE_LENGTH = 6U; @@ -41,11 +44,11 @@ static unsigned int TRAILER_DATA_LENGTH = 12U; static const unsigned char HEADER_MASK = 0x80; static const unsigned char TRAILER_MASK = 0x40; -wxString CDVTOOLFileWriter::m_dirName = wxEmptyString; +std::string CDVTOOLFileWriter::m_dirName = std::string(); CDVTOOLFileWriter::CDVTOOLFileWriter() : m_fileName(), -m_file(), +m_file(nullptr), m_count(0U), m_sequence(0U), m_offset(0) @@ -56,54 +59,59 @@ CDVTOOLFileWriter::~CDVTOOLFileWriter() { } -void CDVTOOLFileWriter::setDirectory(const wxString& dirName) +void CDVTOOLFileWriter::setDirectory(const std::string& dirName) { m_dirName = dirName; } -wxString CDVTOOLFileWriter::getFileName() const +std::string CDVTOOLFileWriter::getFileName() const { return m_fileName; } -bool CDVTOOLFileWriter::open(const wxString& filename, const CHeaderData& header) +bool CDVTOOLFileWriter::open(const std::string& filename, const CHeaderData& header) { - if (m_file.IsOpened()) + if (m_file != nullptr) close(); - wxString name = filename; -#if !defined(__WINDOWS__) - name.Replace(wxT(" "), wxT("_")); -#endif + std::string name = filename; + // Replace spaces with underscores + size_t pos = 0; + while ((pos = name.find(' ', pos)) != std::string::npos) { + name.replace(pos, 1, "_"); + pos += 1; + } - wxFileName fileName(m_dirName, name, wxT("dvtool")); - m_fileName = fileName.GetFullPath(); + m_fileName = m_dirName + "/" + name + ".dvtool"; - bool res = m_file.Open(m_fileName, wxT("wb")); - if (!res) + m_file = ::fopen(m_fileName.c_str(), "wb"); + if (m_file == nullptr) return false; - size_t n = m_file.Write(DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH); + size_t n = ::fwrite(DVTOOL_SIGNATURE, 1U, DVTOOL_SIGNATURE_LENGTH, m_file); if (n != DVTOOL_SIGNATURE_LENGTH) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } - m_offset = m_file.Tell(); + m_offset = ::ftell(m_file); - wxUint32 dummy = 0U; - n = m_file.Write(&dummy, sizeof(wxUint32)); - if (n != sizeof(wxUint32)) { - m_file.Close(); + uint32_t dummy = 0U; + n = ::fwrite(&dummy, 1U, sizeof(uint32_t), m_file); + if (n != sizeof(uint32_t)) { + ::fclose(m_file); + m_file = nullptr; return false; } m_sequence = 0U; m_count = 0U; - res = writeHeader(header); + bool res = writeHeader(header); if (!res) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } @@ -112,53 +120,75 @@ bool CDVTOOLFileWriter::open(const wxString& filename, const CHeaderData& header bool CDVTOOLFileWriter::open(const CHeaderData& header) { - if (m_file.IsOpened()) + if (m_file != nullptr) close(); - wxDateTime time; - time.SetToCurrent(); - - wxString name = time.Format(wxT("%Y%m%d-%H%M%S-")); - - name.Append(header.getRptCall1()); - name.Append(header.getRptCall2()); - name.Append(header.getYourCall()); - name.Append(header.getMyCall1()); - name.Append(header.getMyCall2()); - -#if !defined(__WINDOWS__) - name.Replace(wxT(" "), wxT("_")); + // Format timestamp + time_t now = ::time(nullptr); +#if defined(_WIN32) + struct tm tm_buf; + localtime_s(&tm_buf, &now); + struct tm* tm_info = &tm_buf; +#else + struct tm tm_buf; + struct tm* tm_info = localtime_r(&now, &tm_buf); #endif - name.Replace(wxT("/"), wxT("-")); + char timeBuf[32]; + ::strftime(timeBuf, sizeof(timeBuf), "%Y%m%d-%H%M%S-", tm_info); + + std::string name = std::string(timeBuf); + name += header.getRptCall1(); + name += header.getRptCall2(); + name += header.getYourCall(); + name += header.getMyCall1(); + name += header.getMyCall2(); + + // Replace spaces with underscores + size_t pos = 0; + while ((pos = name.find(' ', pos)) != std::string::npos) { + name.replace(pos, 1, "_"); + pos += 1; + } + + // Sanitize the filename to prevent path traversal from crafted callsigns + for (size_t i = 0; i < name.size(); i++) { + if (name[i] == '/' || name[i] == '\\') + name[i] = '-'; + } + // Remove any ".." sequences + while ((pos = name.find("..")) != std::string::npos) + name.replace(pos, 2, "__"); - wxFileName fileName(m_dirName, name, wxT("dvtool")); - m_fileName = fileName.GetFullPath(); + m_fileName = m_dirName + "/" + name + ".dvtool"; - bool res = m_file.Open(m_fileName, wxT("wb")); - if (!res) + m_file = ::fopen(m_fileName.c_str(), "wb"); + if (m_file == nullptr) return false; - size_t n = m_file.Write(DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH); + size_t n = ::fwrite(DVTOOL_SIGNATURE, 1U, DVTOOL_SIGNATURE_LENGTH, m_file); if (n != DVTOOL_SIGNATURE_LENGTH) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } - m_offset = m_file.Tell(); + m_offset = ::ftell(m_file); - wxUint32 dummy = 0U; - n = m_file.Write(&dummy, sizeof(wxUint32)); - if (n != sizeof(wxUint32)) { - m_file.Close(); + uint32_t dummy = 0U; + n = ::fwrite(&dummy, 1U, sizeof(uint32_t), m_file); + if (n != sizeof(uint32_t)) { + ::fclose(m_file); + m_file = nullptr; return false; } m_sequence = 0U; m_count = 0U; - res = writeHeader(header); + bool res = writeHeader(header); if (!res) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } @@ -167,45 +197,52 @@ bool CDVTOOLFileWriter::open(const CHeaderData& header) bool CDVTOOLFileWriter::write(const unsigned char* buffer, unsigned int length) { - wxASSERT(buffer != 0); - wxASSERT(length > 0U); - - wxUint16 len = wxUINT16_SWAP_ON_BE(length + 15U); - size_t n = m_file.Write(&len, sizeof(wxUint16)); - if (n != sizeof(wxUint16)) { - m_file.Close(); + assert(buffer != nullptr); + assert(length > 0U); + + // wxUINT16_SWAP_ON_BE produces a little-endian value + uint16_t len = htole16(length + 15U); + size_t n = ::fwrite(&len, 1U, sizeof(uint16_t), m_file); + if (n != sizeof(uint16_t)) { + ::fclose(m_file); + m_file = nullptr; return false; } - n = m_file.Write(DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH); + n = ::fwrite(DSVT_SIGNATURE, 1U, DSVT_SIGNATURE_LENGTH, m_file); if (n != DSVT_SIGNATURE_LENGTH) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } char byte = DATA_FLAG; - n = m_file.Write(&byte, 1U); + n = ::fwrite(&byte, 1U, 1U, m_file); if (n != 1U) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } - n = m_file.Write(FIXED_DATA, FIXED_DATA_LENGTH); + n = ::fwrite(FIXED_DATA, 1U, FIXED_DATA_LENGTH, m_file); if (n != FIXED_DATA_LENGTH) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } - byte = m_sequence; - n = m_file.Write(&byte, 1U); + byte = (char)m_sequence; + n = ::fwrite(&byte, 1U, 1U, m_file); if (n != 1U) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } - n = m_file.Write(buffer, length); + n = ::fwrite(buffer, 1U, length, m_file); if (n != length) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } @@ -221,12 +258,14 @@ void CDVTOOLFileWriter::close() { writeTrailer(); - m_file.Seek(m_offset); + ::fseek(m_file, m_offset, SEEK_SET); - wxUint32 count = wxUINT32_SWAP_ON_LE(m_count); - m_file.Write(&count, sizeof(wxUint32)); + // wxUINT32_SWAP_ON_LE produces a big-endian value + uint32_t count = htobe32(m_count); + ::fwrite(&count, 1U, sizeof(uint32_t), m_file); - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; } bool CDVTOOLFileWriter::writeHeader(const CHeaderData& header) @@ -238,61 +277,67 @@ bool CDVTOOLFileWriter::writeHeader(const CHeaderData& header) buffer[2] = header.getFlag3(); for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[3 + i] = header.getRptCall1().GetChar(i); + buffer[3 + i] = header.getRptCall1()[i]; for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[11 + i] = header.getRptCall2().GetChar(i); + buffer[11 + i] = header.getRptCall2()[i]; for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[19 + i] = header.getYourCall().GetChar(i); + buffer[19 + i] = header.getYourCall()[i]; for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[27 + i] = header.getMyCall1().GetChar(i); + buffer[27 + i] = header.getMyCall1()[i]; for (unsigned int i = 0U; i < SHORT_CALLSIGN_LENGTH; i++) - buffer[35 + i] = header.getMyCall2().GetChar(i); + buffer[35 + i] = header.getMyCall2()[i]; // Get the checksum for the header CCCITTChecksumReverse csum; csum.update(buffer, RADIO_HEADER_LENGTH_BYTES - 2U); csum.result(buffer + 39U); - wxUint16 len = wxUINT16_SWAP_ON_BE(RADIO_HEADER_LENGTH_BYTES + 15U); - size_t n = m_file.Write(&len, sizeof(wxUint16)); - if (n != sizeof(wxUint16)) { - m_file.Close(); + uint16_t len = htole16(RADIO_HEADER_LENGTH_BYTES + 15U); + size_t n = ::fwrite(&len, 1U, sizeof(uint16_t), m_file); + if (n != sizeof(uint16_t)) { + ::fclose(m_file); + m_file = nullptr; return false; } - n = m_file.Write(DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH); + n = ::fwrite(DSVT_SIGNATURE, 1U, DSVT_SIGNATURE_LENGTH, m_file); if (n != DSVT_SIGNATURE_LENGTH) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } char byte = HEADER_FLAG; - n = m_file.Write(&byte, 1U); + n = ::fwrite(&byte, 1U, 1U, m_file); if (n != 1U) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } - n = m_file.Write(FIXED_DATA, FIXED_DATA_LENGTH); + n = ::fwrite(FIXED_DATA, 1U, FIXED_DATA_LENGTH, m_file); if (n != FIXED_DATA_LENGTH) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } byte = HEADER_MASK; - n = m_file.Write(&byte, 1U); + n = ::fwrite(&byte, 1U, 1U, m_file); if (n != 1U) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } - n = m_file.Write(buffer, RADIO_HEADER_LENGTH_BYTES); + n = ::fwrite(buffer, 1U, RADIO_HEADER_LENGTH_BYTES, m_file); if (n != RADIO_HEADER_LENGTH_BYTES) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } @@ -303,42 +348,48 @@ bool CDVTOOLFileWriter::writeHeader(const CHeaderData& header) bool CDVTOOLFileWriter::writeTrailer() { - wxUint16 len = wxUINT16_SWAP_ON_BE(27U); - size_t n = m_file.Write(&len, sizeof(wxUint16)); - if (n != sizeof(wxUint16)) { - m_file.Close(); + uint16_t len = htole16(27U); + size_t n = ::fwrite(&len, 1U, sizeof(uint16_t), m_file); + if (n != sizeof(uint16_t)) { + ::fclose(m_file); + m_file = nullptr; return false; } - n = m_file.Write(DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH); + n = ::fwrite(DSVT_SIGNATURE, 1U, DSVT_SIGNATURE_LENGTH, m_file); if (n != DSVT_SIGNATURE_LENGTH) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } char byte = DATA_FLAG; - n = m_file.Write(&byte, 1U); + n = ::fwrite(&byte, 1U, 1U, m_file); if (n != 1U) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } - n = m_file.Write(FIXED_DATA, FIXED_DATA_LENGTH); + n = ::fwrite(FIXED_DATA, 1U, FIXED_DATA_LENGTH, m_file); if (n != FIXED_DATA_LENGTH) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } - byte = TRAILER_MASK | m_sequence; - n = m_file.Write(&byte, 1U); + byte = (char)(TRAILER_MASK | m_sequence); + n = ::fwrite(&byte, 1U, 1U, m_file); if (n != 1U) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } - n = m_file.Write(TRAILER_DATA, TRAILER_DATA_LENGTH); + n = ::fwrite(TRAILER_DATA, 1U, TRAILER_DATA_LENGTH, m_file); if (n != TRAILER_DATA_LENGTH) { - m_file.Close(); + ::fclose(m_file); + m_file = nullptr; return false; } diff --git a/Common/DVTOOLFileWriter.h b/Common/DVTOOLFileWriter.h index d771614..7c5fdbb 100644 --- a/Common/DVTOOLFileWriter.h +++ b/Common/DVTOOLFileWriter.h @@ -21,31 +21,52 @@ #include "HeaderData.h" -#include -#include +#include "StdCompat.h" +#include +#include +/* + * Sequential writer for .dvtool recording files (see DVTOOLFileReader.h for + * the file format description). + * + * Usage: + * open() — writes the "DVTOOL" signature and a placeholder record count, + * then writes the header record. + * write() — appends each DV data frame as a DSVT data record. + * close() — writes the trailer record, then seeks back to the placeholder + * and fills in the final record count (big-endian uint32). + * + * Two open() overloads are provided: + * open(filename, header) — uses a caller-supplied base filename. + * open(header) — auto-generates a filename from a timestamp and + * the callsigns embedded in the header. + * + * setDirectory() is a class-level (static) setting that prepends a directory + * to all generated filenames. + */ class CDVTOOLFileWriter { public: CDVTOOLFileWriter(); ~CDVTOOLFileWriter(); - static void setDirectory(const wxString& dirName); + // Sets the output directory used by all CDVTOOLFileWriter instances. + static void setDirectory(const std::string& dirName); - wxString getFileName() const; + std::string getFileName() const; bool open(const CHeaderData& header); - bool open(const wxString& filename, const CHeaderData& header); + bool open(const std::string& filename, const CHeaderData& header); bool write(const unsigned char* buffer, unsigned int length); void close(); private: - static wxString m_dirName; + static std::string m_dirName; - wxString m_fileName; - wxFFile m_file; - wxUint32 m_count; - unsigned int m_sequence; - wxFileOffset m_offset; + std::string m_fileName; + FILE* m_file; + uint32_t m_count; // Number of DSVT records written (header + data + trailer). + unsigned int m_sequence; // Per-frame sequence counter (0–20, mirrors DSRP wire format). + long m_offset; // File offset of the record-count field, for back-patching in close(). bool writeHeader(const CHeaderData& header); bool writeTrailer(); diff --git a/Common/DummyController.cpp b/Common/DummyController.cpp index 3f14c0b..60a3344 100644 --- a/Common/DummyController.cpp +++ b/Common/DummyController.cpp @@ -13,8 +13,6 @@ #include "DummyController.h" -#include - CDummyController::CDummyController() { } diff --git a/Common/EndianCompat.h b/Common/EndianCompat.h new file mode 100644 index 0000000..7679eb1 --- /dev/null +++ b/Common/EndianCompat.h @@ -0,0 +1,35 @@ +/* + * Cross-platform byte-swap functions for the D-Star wire format. + * + * D-Star protocol fields are little-endian on the wire. This header provides + * htole16/le16toh/htole32/le32toh/htobe32/be32toh using the appropriate + * platform API (POSIX on Linux, OSByteOrder on macOS, and + * _byteswap intrinsics on Windows x86). Include this wherever protocol + * buffers are serialised or deserialised. + */ + +#ifndef EndianCompat_H +#define EndianCompat_H + +#if defined(_WIN32) +#include +// Windows: use _byteswap intrinsics +static inline uint16_t htole16(uint16_t x) { return x; } // x86 is little-endian +static inline uint16_t le16toh(uint16_t x) { return x; } +static inline uint32_t htole32(uint32_t x) { return x; } +static inline uint32_t le32toh(uint32_t x) { return x; } +static inline uint32_t htobe32(uint32_t x) { return _byteswap_ulong(x); } +static inline uint32_t be32toh(uint32_t x) { return _byteswap_ulong(x); } +#elif defined(__APPLE__) +#include +#define htole16(x) OSSwapHostToLittleInt16(x) +#define le16toh(x) OSSwapLittleToHostInt16(x) +#define htole32(x) OSSwapHostToLittleInt32(x) +#define le32toh(x) OSSwapLittleToHostInt32(x) +#define htobe32(x) OSSwapHostToBigInt32(x) +#define be32toh(x) OSSwapBigToHostInt32(x) +#else +#include +#endif + +#endif diff --git a/Common/ExternalController.cpp b/Common/ExternalController.cpp index fdb12be..0db1fd9 100644 --- a/Common/ExternalController.cpp +++ b/Common/ExternalController.cpp @@ -15,8 +15,10 @@ #include "DStarDefines.h" +#include +#include + CExternalController::CExternalController(IHardwareController* controller, bool pttInvert) : -wxThread(wxTHREAD_JOINABLE), m_controller(controller), m_pttInvert(pttInvert), m_disable(false), @@ -27,10 +29,11 @@ m_out1(false), m_out2(false), m_out3(false), m_out4(false), -m_kill(false) +m_kill(false), +m_thread() { - // wxASSERT(controller != NULL); - + // When PTT is active-low the idle state is high (i.e. inverted true), so + // pre-set m_radioTX accordingly before the thread starts writing to hardware. if (m_pttInvert) m_radioTX = true; } @@ -46,42 +49,45 @@ bool CExternalController::open() if (!res) return false; - Create(); - Run(); + m_thread = std::thread(&CExternalController::entry, this); return true; } -void* CExternalController::Entry() +void CExternalController::entry() { - wxASSERT(m_controller != NULL); - - bool dummy1, dummy2, dummy3, dummy4; + bool dummy1, dummy2, dummy3, dummy4, disableIn; + // Poll the hardware at half the D-Star frame rate (every 10 ms). + // Apply desired output states, then latch the disable input for the repeater. while (!m_kill) { m_controller->setDigitalOutputs(m_radioTX, false, m_heartbeat, m_active, m_out1, m_out2, m_out3, m_out4); - m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, m_disable); + m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, disableIn); + m_disable = disableIn; - Sleep(DSTAR_FRAME_TIME_MS / 2U); + std::this_thread::sleep_for(std::chrono::milliseconds(DSTAR_FRAME_TIME_MS / 2U)); } + // Shutdown sequence: de-assert PTT (accounting for inversion), wait for the + // transmitter to drop (3 frame periods = 60 ms), then confirm the idle state + // a second time before closing the hardware device. if (m_pttInvert) m_controller->setDigitalOutputs(true, false, false, false, false, false, false, false); else m_controller->setDigitalOutputs(false, false, false, false, false, false, false, false); - m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, m_disable); + m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, disableIn); + m_disable = disableIn; - Sleep(DSTAR_FRAME_TIME_MS * 3U); + std::this_thread::sleep_for(std::chrono::milliseconds(DSTAR_FRAME_TIME_MS * 3U)); if (m_pttInvert) m_controller->setDigitalOutputs(true, false, false, false, false, false, false, false); else m_controller->setDigitalOutputs(false, false, false, false, false, false, false, false); - m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, m_disable); + m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, disableIn); + m_disable = disableIn; m_controller->close(); - - return NULL; } bool CExternalController::getDisable() const @@ -99,6 +105,7 @@ void CExternalController::setRadioTransmit(bool value) void CExternalController::setHeartbeat() { + // Toggle on each call so any periodic caller drives a visible LED blink. m_heartbeat = !m_heartbeat; } @@ -131,5 +138,6 @@ void CExternalController::close() { m_kill = true; - Wait(); + if (m_thread.joinable()) + m_thread.join(); } diff --git a/Common/ExternalController.h b/Common/ExternalController.h index 6a804f5..1d12663 100644 --- a/Common/ExternalController.h +++ b/Common/ExternalController.h @@ -11,46 +11,83 @@ * GNU General Public License for more details. */ +/* + * CExternalController wraps an IHardwareController (serial port, Arduino, + * GPIO board, etc.) and drives it from a dedicated background thread. + * + * The repeater thread calls the set* methods to express the desired output + * state (PTT, heartbeat LED, active indicator, auxiliary outputs). The + * internal thread polls the hardware at half the D-Star frame rate (every + * 10 ms) and applies those values to the physical I/O pins. Input pins + * (the "disable" line) are read on the same schedule and stored atomically + * so the repeater thread can check them without blocking. + * + * PTT inversion + * ------------- + * Some hardware asserts PTT by pulling a line low rather than high. When + * pttInvert is true the radioTX sense is flipped before being written to the + * hardware, and the idle state is initialised to the inverted (asserted) value + * so the line is de-asserted once the thread starts. + * + * Shutdown sequence + * ----------------- + * close() sets m_kill and joins the thread. The thread then de-asserts PTT, + * waits three frame periods (60 ms) to let the transmitter drop, asserts the + * final idle state once more, then calls m_controller->close(). + */ + #ifndef ExternalController_H #define ExternalController_H #include "HardwareController.h" +#include "StdCompat.h" -#include +#include +#include -class CExternalController : public wxThread { +class CExternalController { public: CExternalController(IHardwareController* controller, bool pttInvert); virtual ~CExternalController(); + // Open the hardware device and start the polling thread. virtual bool open(); + // True when the hardware disable input is asserted (repeater should not TX). virtual bool getDisable() const; + // Set the desired PTT state; inverted before applying if pttInvert is set. virtual void setRadioTransmit(bool value); + // Toggle the heartbeat output each call (caller drives the toggle rate). virtual void setHeartbeat(); + // Assert or de-assert the "active" status output. virtual void setActive(bool value); virtual void setOutput1(bool value); virtual void setOutput2(bool value); virtual void setOutput3(bool value); virtual void setOutput4(bool value); + // Signal the polling thread to stop, then close the hardware device. virtual void close(); - virtual void* Entry(); - private: - IHardwareController* m_controller; - bool m_pttInvert; - bool m_disable; - bool m_heartbeat; - bool m_active; - bool m_radioTX; - bool m_out1; - bool m_out2; - bool m_out3; - bool m_out4; - bool m_kill; + void entry(); // Polling thread body + + IHardwareController* m_controller; // Concrete hardware driver (owned) + bool m_pttInvert; // True if PTT sense is active-low + + // Desired output states written by the repeater thread and read by entry(). + // All are atomic so no mutex is needed for the shared flag pattern. + std::atomic m_disable; // Latched from the hardware disable input + std::atomic m_heartbeat; // Heartbeat/LED toggle + std::atomic m_active; // Repeater-active indicator + std::atomic m_radioTX; // PTT output (post-inversion) + std::atomic m_out1; // Auxiliary outputs 1–4 + std::atomic m_out2; + std::atomic m_out3; + std::atomic m_out4; + std::atomic m_kill; // Set by close() to stop the poll loop + std::thread m_thread; }; #endif diff --git a/Common/FIRFilter.cpp b/Common/FIRFilter.cpp index c397b49..aaac0a7 100644 --- a/Common/FIRFilter.cpp +++ b/Common/FIRFilter.cpp @@ -14,27 +14,30 @@ #include "FIRFilter.h" -CFIRFilter::CFIRFilter(const wxFloat32* taps, unsigned int length) : -m_taps(NULL), +#include +#include + +CFIRFilter::CFIRFilter(const float* taps, unsigned int length) : +m_taps(nullptr), m_length(length), -m_buffer(NULL), +m_buffer(nullptr), m_bufLen(20U * m_length), m_pointer(length) { - wxASSERT(taps != NULL); - wxASSERT(length > 0U); + assert(taps != nullptr); + assert(length > 0U); - m_taps = new wxFloat32[m_length]; - m_buffer = new wxFloat32[m_bufLen]; + m_taps = new float[m_length]; + m_buffer = new float[m_bufLen]; - ::memcpy(m_taps, taps, m_length * sizeof(wxFloat32)); - ::memset(m_buffer, 0x00, m_bufLen * sizeof(wxFloat32)); + ::memcpy(m_taps, taps, m_length * sizeof(float)); + ::memset(m_buffer, 0x00, m_bufLen * sizeof(float)); } CFIRFilter::CFIRFilter() : -m_taps(NULL), +m_taps(nullptr), m_length(0U), -m_buffer(NULL), +m_buffer(nullptr), m_bufLen(0U), m_pointer(0U) { @@ -46,10 +49,10 @@ CFIRFilter::~CFIRFilter() delete[] m_buffer; } -void CFIRFilter::setTaps(const wxFloat32* taps, unsigned int length) +void CFIRFilter::setTaps(const float* taps, unsigned int length) { - wxASSERT(taps != NULL); - wxASSERT(length > 0U); + assert(taps != nullptr); + assert(length > 0U); delete[] m_taps; delete[] m_buffer; @@ -58,46 +61,46 @@ void CFIRFilter::setTaps(const wxFloat32* taps, unsigned int length) m_pointer = length; m_bufLen = 20U * m_length; - m_taps = new wxFloat32[m_length]; - m_buffer = new wxFloat32[m_bufLen]; + m_taps = new float[m_length]; + m_buffer = new float[m_bufLen]; - ::memcpy(m_taps, taps, m_length * sizeof(wxFloat32)); - ::memset(m_buffer, 0x00, m_bufLen * sizeof(wxFloat32)); + ::memcpy(m_taps, taps, m_length * sizeof(float)); + ::memset(m_buffer, 0x00, m_bufLen * sizeof(float)); } -wxFloat32 CFIRFilter::process(wxFloat32 val) +float CFIRFilter::process(float val) { - wxFloat32* ptr = m_buffer + m_pointer++; + float* ptr = m_buffer + m_pointer++; *ptr = val; - wxFloat32* a = ptr - m_length; - wxFloat32* b = m_taps; + float* a = ptr - m_length; + float* b = m_taps; - wxFloat32 out = 0.0F; + float out = 0.0F; for (unsigned int i = 0U; i < m_length; i++) out += (*a++) * (*b++); if (m_pointer == m_bufLen) { - ::memcpy(m_buffer, m_buffer + m_bufLen - m_length, m_length * sizeof(wxFloat32)); + ::memcpy(m_buffer, m_buffer + m_bufLen - m_length, m_length * sizeof(float)); m_pointer = m_length; } return out; } -void CFIRFilter::process(wxFloat32* inOut, unsigned int length) +void CFIRFilter::process(float* inOut, unsigned int length) { - wxASSERT(inOut != NULL); + assert(inOut != nullptr); for (unsigned int i = 0U; i < length; i++) inOut[i] = process(inOut[i]); } -void CFIRFilter::process(const wxFloat32* in, wxFloat32* out, unsigned int length) +void CFIRFilter::process(const float* in, float* out, unsigned int length) { - wxASSERT(in != NULL); - wxASSERT(out != NULL); + assert(in != nullptr); + assert(out != nullptr); for (unsigned int i = 0U; i < length; i++) out[i] = process(in[i]); @@ -105,5 +108,5 @@ void CFIRFilter::process(const wxFloat32* in, wxFloat32* out, unsigned int lengt void CFIRFilter::reset() { - ::memset(m_buffer, 0x00, m_bufLen * sizeof(wxFloat32));; + ::memset(m_buffer, 0x00, m_bufLen * sizeof(float)); } diff --git a/Common/FIRFilter.h b/Common/FIRFilter.h index b164212..dbfe716 100644 --- a/Common/FIRFilter.h +++ b/Common/FIRFilter.h @@ -14,26 +14,33 @@ #ifndef FIRFilter_H #define FIRFilter_H -#include - +/* + * Direct-form FIR (Finite Impulse Response) filter operating on float samples. + * + * Used by the GMSK modulator and demodulator to implement the Gaussian pulse- + * shaping filter required by the D-Star GMSK modulation scheme (BT=0.5). + * + * process() convolves input samples with the tap coefficients using a circular + * delay buffer. reset() clears the delay line without changing the tap set. + */ class CFIRFilter { public: - CFIRFilter(const wxFloat32* taps, unsigned int length); + CFIRFilter(const float* taps, unsigned int length); CFIRFilter(); ~CFIRFilter(); - void setTaps(const wxFloat32* taps, unsigned int length); + void setTaps(const float* taps, unsigned int length); - wxFloat32 process(wxFloat32 val); - void process(wxFloat32* inOut, unsigned int length); - void process(const wxFloat32* in, wxFloat32* out, unsigned int length); + float process(float val); + void process(float* inOut, unsigned int length); + void process(const float* in, float* out, unsigned int length); - void reset(); + void reset(); private: - wxFloat32* m_taps; + float* m_taps; unsigned int m_length; - wxFloat32* m_buffer; + float* m_buffer; // Circular delay line for the filter state. unsigned int m_bufLen; unsigned int m_pointer; }; diff --git a/Common/GMSKController.cpp b/Common/GMSKController.cpp index 00d8162..fec9935 100644 --- a/Common/GMSKController.cpp +++ b/Common/GMSKController.cpp @@ -17,11 +17,13 @@ */ #include "GMSKController.h" -#if defined(__WINDOWS__) -#include "GMSKModemWinUSB.h" -#endif #include "GMSKModemLibUsb.h" #include "Timer.h" +#include "Logger.h" + +#include +#include +#include const unsigned char DVRPTR_HEADER_LENGTH = 5U; @@ -31,30 +33,16 @@ const unsigned int CYCLE_TIME = 15U; CGMSKController::CGMSKController(USB_INTERFACE iface, unsigned int address, bool duplex) : CModem(), -m_modem(NULL), +m_modem(nullptr), m_duplex(duplex), -m_buffer(NULL), +m_buffer(nullptr), m_txData(1000U) { - wxASSERT(address > 0U); + assert(address > 0U); m_buffer = new unsigned char[BUFFER_LENGTH]; -#if defined(__WINDOWS__) - switch (iface) { - case UI_LIBUSB: - m_modem = new CGMSKModemLibUsb(address); - break; - case UI_WINUSB: - m_modem = new CGMSKModemWinUSB(address); - break; - default: - wxLogError(wxT("Unknown GMSM modem driver type - %d"), int(iface)); - break; - } -#else m_modem = new CGMSKModemLibUsb(address); -#endif } CGMSKController::~CGMSKController() @@ -64,7 +52,7 @@ CGMSKController::~CGMSKController() bool CGMSKController::start() { - if (m_modem == NULL) + if (m_modem == nullptr) return false; bool ret = m_modem->open(); @@ -73,16 +61,14 @@ bool CGMSKController::start() return false; } - Create(); - SetPriority(100U); - Run(); + m_thread = std::thread(&CGMSKController::entry, this); return true; } -void* CGMSKController::Entry() +void CGMSKController::entry() { - wxLogMessage(wxT("Starting GMSK Modem Controller thread")); + wxLogMessage("Starting GMSK Modem Controller thread"); CTimer hdrTimer(1000U, 0U, 100U); hdrTimer.start(); @@ -98,10 +84,8 @@ void* CGMSKController::Entry() unsigned char readLength = 0U; unsigned char* readBuffer = new unsigned char[DV_FRAME_LENGTH_BYTES]; - wxStopWatch stopWatch; - while (!m_stopped) { - stopWatch.Start(); + auto loopStart = std::chrono::steady_clock::now(); // Only receive when not transmitting or when in duplex mode if (!m_tx || m_duplex) { @@ -110,10 +94,10 @@ void* CGMSKController::Entry() bool end; int ret = m_modem->readData(buffer, GMSK_MODEM_DATA_LENGTH, end); if (ret >= 0) { - // CUtils::dump(wxT("Read Data"), buffer, ret); + // CUtils::dump("Read Data", buffer, ret); if (end) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; @@ -129,7 +113,7 @@ void* CGMSKController::Entry() readLength++; if (readLength >= DV_FRAME_LENGTH_BYTES) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_DATA; @@ -152,9 +136,9 @@ void* CGMSKController::Entry() unsigned char buffer[90U]; bool ret = m_modem->readHeader(buffer, 90U); if (ret) { - // CUtils::dump(wxT("Read Header"), buffer, RADIO_HEADER_LENGTH_BYTES); + // CUtils::dump("Read Header", buffer, RADIO_HEADER_LENGTH_BYTES); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_HEADER; @@ -176,7 +160,7 @@ void* CGMSKController::Entry() // Only transmit when not receiving or when in duplex mode if (!rx || m_duplex) { if (writeLength == 0U && m_txData.hasData()) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char type = DSMTT_NONE; m_txData.getData(&type, 1U); @@ -192,7 +176,7 @@ void* CGMSKController::Entry() // Check that the modem isn't still transmitting before sending the new header if (tx == STATE_FALSE) { - // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + // CUtils::dump("Write Header", writeBuffer, writeLength); m_modem->writeHeader(writeBuffer, writeLength); m_modem->setPTT(true); dataTimer.start(); @@ -212,7 +196,7 @@ void* CGMSKController::Entry() // Check that there is space in the modem buffer if (ret == STATE_TRUE) { - // CUtils::dump(wxT("Write Data"), writeBuffer, writeLength); + // CUtils::dump("Write Data", writeBuffer, writeLength); int ret = m_modem->writeData(writeBuffer, writeLength); if (ret > 0) { writeLength -= ret; @@ -236,14 +220,16 @@ void* CGMSKController::Entry() } } - unsigned long ms = stopWatch.Time(); + auto loopEnd = std::chrono::steady_clock::now(); + unsigned long ms = std::chrono::duration_cast(loopEnd - loopStart).count(); // Don't sleep when reading from the modem if (!rx) { if (ms < CYCLE_TIME) - Sleep(CYCLE_TIME - ms); + std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_TIME - ms)); - ms = stopWatch.Time(); + loopEnd = std::chrono::steady_clock::now(); + ms = std::chrono::duration_cast(loopEnd - loopStart).count(); } // Catch up with the clock @@ -251,24 +237,22 @@ void* CGMSKController::Entry() hdrTimer.clock(ms); } - wxLogMessage(wxT("Stopping GMSK Modem Controller thread")); + wxLogMessage("Stopping GMSK Modem Controller thread"); - if (m_modem != NULL) { + if (m_modem != nullptr) { m_modem->close(); delete m_modem; } delete[] writeBuffer; delete[] readBuffer; - - return NULL; } bool CGMSKController::writeHeader(const CHeaderData& header) { bool ret = m_txData.hasSpace(RADIO_HEADER_LENGTH_BYTES); if (!ret) { - wxLogWarning(wxT("No space to write the header")); + wxLogWarning("No space to write the header"); return false; } @@ -280,27 +264,27 @@ bool CGMSKController::writeHeader(const CHeaderData& header) buffer[1U] = header.getFlag2(); buffer[2U] = header.getFlag3(); - wxString rpt2 = header.getRptCall2(); - for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 3U] = rpt2.GetChar(i); + std::string rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 3U] = rpt2[i]; - wxString rpt1 = header.getRptCall1(); - for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 11U] = rpt1.GetChar(i); + std::string rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 11U] = rpt1[i]; - wxString your = header.getYourCall(); - for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 19U] = your.GetChar(i); + std::string your = header.getYourCall(); + for (unsigned int i = 0U; i < your.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 19U] = your[i]; - wxString my1 = header.getMyCall1(); - for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 27U] = my1.GetChar(i); + std::string my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 27U] = my1[i]; - wxString my2 = header.getMyCall2(); - for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) - buffer[i + 35U] = my2.GetChar(i); + std::string my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.size() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 35U] = my2[i]; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_HEADER; @@ -316,11 +300,11 @@ bool CGMSKController::writeData(const unsigned char* data, unsigned int, bool en { bool ret = m_txData.hasSpace(DV_FRAME_LENGTH_BYTES + 2U); if (!ret) { - wxLogWarning(wxT("No space to write data")); + wxLogWarning("No space to write data"); return false; } - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char buffer[2U]; buffer[0U] = end ? DSMTT_EOT : DSMTT_DATA; @@ -347,7 +331,7 @@ bool CGMSKController::isTXReady() bool CGMSKController::reopenModem() { - wxLogMessage(wxT("Connection to the GMSK modem has been lost")); + wxLogMessage("Connection to the GMSK modem has been lost"); m_modem->close(); @@ -357,18 +341,18 @@ bool CGMSKController::reopenModem() return true; // Reset the drivers state - m_mutex.Lock(); - m_txData.clear(); - m_mutex.Unlock(); + { + std::lock_guard lock(m_mutex); + m_txData.clear(); + } m_tx = false; - Sleep(1000UL); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } delete m_modem; - m_modem = NULL; + m_modem = nullptr; return false; - } diff --git a/Common/GMSKController.h b/Common/GMSKController.h index 6eab923..d45ae52 100644 --- a/Common/GMSKController.h +++ b/Common/GMSKController.h @@ -24,16 +24,36 @@ #include "GMSKModem.h" #include "Modem.h" #include "Utils.h" +#include "StdCompat.h" -#include - +/* + * CGMSKController - CModem driver that bridges the repeater to an IGMSKModem. + * + * Owns an IGMSKModem* (currently always a CGMSKModemLibUsb instance) and + * presents the standard CModem interface to the repeater thread. + * + * entry() loop (runs ~15ms cycle): + * Receive side: polls readHeader() every 100ms when idle. On a valid header, + * switches to data mode and calls readData() every loop until end is signalled. + * Received bytes are accumulated in a local buffer until a full DV_FRAME_LENGTH + * (12 bytes) is available, then forwarded to m_rxData. + * + * Transmit side: reads type/length/data from m_txData ring buffer. For headers, + * waits until the hardware PTT is deasserted before calling writeHeader() + + * setPTT(true). For data frames, waits 100ms after the header (dataTimer) then + * polls hasSpace() before each writeData(). On EOT, calls setPTT(false). + * + * duplex: when true, RX and TX can occur simultaneously. When false, TX + * suppresses RX polling and vice versa. + * + * reopenModem(): called on any negative return from an IGMSKModem method. + * Closes the modem, clears m_txData, and retries open() every 1s indefinitely. + */ class CGMSKController : public CModem { public: CGMSKController(USB_INTERFACE iface, unsigned int address, bool duplex); virtual ~CGMSKController(); - virtual void* Entry(); - virtual bool start(); virtual unsigned int getSpace(); @@ -43,6 +63,8 @@ public: virtual bool writeData(const unsigned char* data, unsigned int length, bool end); private: + void entry(); + IGMSKModem* m_modem; bool m_duplex; unsigned char* m_buffer; diff --git a/Common/GMSKModem.h b/Common/GMSKModem.h index 7c1a8c3..e6cdaf4 100644 --- a/Common/GMSKModem.h +++ b/Common/GMSKModem.h @@ -21,8 +21,20 @@ #include "Utils.h" -#include - +/* + * IGMSKModem - Abstract interface for GMSK modem hardware. + * + * Implemented by CGMSKModemLibUsb (libusb) and potentially other backends. + * CGMSKController owns an IGMSKModem* and drives it through this interface, + * so the controller is independent of the USB transport details. + * + * The interface divides into three functional groups: + * Lifecycle: open(), close() + * Receive: readHeader(), readData() - poll-based; called from controller loop + * Transmit: writeHeader(), writeData(), setPTT() - push-based from controller + * Status: getPTT(), hasSpace() - return TRISTATE (TRUE/FALSE/UNKNOWN) + * UNKNOWN indicates a hardware error requiring modem reconnection. + */ class IGMSKModem { public: virtual ~IGMSKModem() = 0; @@ -46,4 +58,3 @@ private: }; #endif - diff --git a/Common/GMSKModemLibUsb.cpp b/Common/GMSKModemLibUsb.cpp index d552be0..2aa2468 100644 --- a/Common/GMSKModemLibUsb.cpp +++ b/Common/GMSKModemLibUsb.cpp @@ -18,6 +18,12 @@ #include "GMSKModemLibUsb.h" #include "DStarDefines.h" +#include "Logger.h" + +#include +#include +#include +#include const unsigned int VENDOR_ID = 0x04D8U; @@ -46,118 +52,48 @@ const char PTT_OnOff = 0x20; const int PTT_ON = 1; const int PTT_OFF = 0; -#if defined(WIN32) -const wxString LIBNAME = wxT("libusb0"); - -wxDynamicLibrary* CGMSKModemLibUsb::m_library = NULL; -bool CGMSKModemLibUsb::m_loaded = false; - -void (*CGMSKModemLibUsb::m_usbInit)() = NULL; -int (*CGMSKModemLibUsb::m_usbFindBusses)() = NULL; -int (*CGMSKModemLibUsb::m_usbFindDevices)() = NULL; -usb_bus* (*CGMSKModemLibUsb::m_usbGetBusses)() = NULL; -usb_dev_handle* (*CGMSKModemLibUsb::m_usbOpen)(struct usb_device*) = NULL; -int (*CGMSKModemLibUsb::m_usbSetConfiguration)(usb_dev_handle*, int) = NULL; -int (*CGMSKModemLibUsb::m_usbControlMsg)(usb_dev_handle*, int, int, int, int, unsigned char*, int, int) = NULL; -char* (*CGMSKModemLibUsb::m_usbStrerror)() = NULL; -int (*CGMSKModemLibUsb::m_usbClose)(usb_dev_handle*) = NULL; -#endif - static void libUsbLogError(int ret, const char *message) { -#if defined(WIN32) - wxString errorText(CGMSKModemLibUsb::m_usbStrerror(), wxConvLocal); -#else - wxString errorText(libusb_error_name(ret), wxConvLocal); -#endif - wxLogMessage(wxT("%s, ret: %d, err=%s"), message, ret, errorText.c_str()); + std::string errorText(libusb_error_name(ret)); + wxLogMessage("%s, ret: %d, err=%s", message, ret, errorText.c_str()); } CGMSKModemLibUsb::CGMSKModemLibUsb(unsigned int address) : m_address(address), -#if !defined(WIN32) -m_context(NULL), -#endif -m_dev(NULL), +m_context(nullptr), +m_dev(nullptr), m_brokenSpace(false) { - -#if defined(WIN32) - if (m_library == NULL) - m_library = new wxDynamicLibrary(LIBNAME); - - if (!m_library->IsLoaded()) { - wxLogError(wxT("Unable to load shared library %s"), LIBNAME.c_str()); - return; - } - - void* ptr1 = m_library->GetSymbol(wxT("usb_init")); - void* ptr2 = m_library->GetSymbol(wxT("usb_find_busses")); - void* ptr3 = m_library->GetSymbol(wxT("usb_find_devices")); - void* ptr4 = m_library->GetSymbol(wxT("usb_get_busses")); - void* ptr5 = m_library->GetSymbol(wxT("usb_open")); - void* ptr6 = m_library->GetSymbol(wxT("usb_set_configuration")); - void* ptr7 = m_library->GetSymbol(wxT("usb_control_msg")); - void* ptr8 = m_library->GetSymbol(wxT("usb_strerror")); - void* ptr9 = m_library->GetSymbol(wxT("usb_close")); - - if (ptr1 == NULL || ptr2 == NULL || ptr3 == NULL || - ptr4 == NULL || ptr5 == NULL || ptr6 == NULL || - ptr7 == NULL || ptr8 == NULL || ptr9 == NULL) { - wxLogError(wxT("Unable to get symbols from %s"), LIBNAME.c_str()); - return; - } - - m_usbInit = (void (*)())ptr1; - m_usbFindBusses = (int (*)())ptr2; - m_usbFindDevices = (int (*)())ptr3; - m_usbGetBusses = (usb_bus* (*)())ptr4; - m_usbOpen = (usb_dev_handle* (*)(struct usb_device*))ptr5; - m_usbSetConfiguration = (int (*)(usb_dev_handle*, int))ptr6; - m_usbControlMsg = (int (*)(usb_dev_handle*, int, int, int, int, unsigned char*, int, int))ptr7; - m_usbStrerror = (char* (*)())ptr8; - m_usbClose = (int (*)(usb_dev_handle*))ptr9; - - wxLogMessage(wxT("Successfully loaded library %s"), LIBNAME.c_str()); - - m_loaded = true; -#else ::libusb_init(&m_context); -#endif } CGMSKModemLibUsb::~CGMSKModemLibUsb() { -#if !defined(WIN32) - wxASSERT(m_context != NULL); + assert(m_context != nullptr); ::libusb_exit(m_context); -#endif } bool CGMSKModemLibUsb::open() { -#if !defined(WIN32) - wxASSERT(m_context != NULL); -#endif - wxASSERT(m_dev == NULL); + assert(m_context != nullptr); + assert(m_dev == nullptr); bool ret1 = openModem(); if (!ret1) { - wxLogError(wxT("Cannot find the GMSK Modem with address: 0x%04X"), m_address); + wxLogError("Cannot find the GMSK Modem with address: 0x%04X", m_address); return false; } - wxLogInfo(wxT("Found the GMSK Modem with address: 0x%04X"), m_address); + wxLogInfo("Found the GMSK Modem with address: 0x%04X", m_address); - wxString version; + std::string version; int ret2; do { unsigned char buffer[GMSK_MODEM_DATA_LENGTH]; ret2 = io(0xC0, GET_VERSION, 0, 0, buffer, GMSK_MODEM_DATA_LENGTH, USB_TIMEOUT); if (ret2 > 0) { - wxString text((char *) buffer, wxConvLocal, ret2); - version.Append(text); + version.append((char*)buffer, ret2); } else if (ret2 < 0) { ::libUsbLogError(ret2, "GET_VERSION"); close(); @@ -165,27 +101,34 @@ bool CGMSKModemLibUsb::open() } } while (ret2 == int(GMSK_MODEM_DATA_LENGTH)); - wxLogInfo(wxT("Firmware version: %s"), version.c_str()); + wxLogInfo("Firmware version: %s", version.c_str()); // Trap firmware version 0.1.00 of DUTCH*Star and complain loudly - if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND && version.Find(wxT("0.1.00")) != wxNOT_FOUND) { - wxLogWarning(wxT("This modem firmware is not supported by the repeater")); - wxLogWarning(wxT("Please upgrade to a newer version")); + if (version.find("DUTCH*Star") != std::string::npos && version.find("0.1.00") != std::string::npos) { + wxLogWarning("This modem firmware is not supported by the repeater"); + wxLogWarning("Please upgrade to a newer version"); close(); return false; } // DUTCH*Star firmware has a broken concept of free space - if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND) + if (version.find("DUTCH*Star") != std::string::npos) m_brokenSpace = true; return true; } +// Polls the modem for a received D-Star header via repeated GET_HEADER transfers. +// Each transfer returns up to 8 bytes. The loop accumulates bytes until +// RADIO_HEADER_LENGTH_BYTES have been received. +// If GET_HEADER returns 0 bytes mid-header and the COS (carrier-operated squelch) +// status bit is still set, the header counter is reset to zero because the signal +// has restarted (e.g. a quick key-up/key-down) and the modem's header buffer has +// been overwritten. Returns false on CRC error (GET_AD_STATUS CRC_ERROR bit set). bool CGMSKModemLibUsb::readHeader(unsigned char* header, unsigned int length) { - wxASSERT(header != NULL); - wxASSERT(length > (RADIO_HEADER_LENGTH_BYTES * 2U)); + assert(header != nullptr); + assert(length > (RADIO_HEADER_LENGTH_BYTES * 2U)); unsigned int offset = 0U; @@ -197,12 +140,12 @@ bool CGMSKModemLibUsb::readHeader(unsigned char* header, unsigned int length) if (ret == -19) // -ENODEV return false; - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } else if (ret == 0) { if (offset == 0U) return false; - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); unsigned char status; int ret = io(0xC0, GET_AD_STATUS, 0, 0, &status, 1, USB_TIMEOUT); @@ -212,7 +155,7 @@ bool CGMSKModemLibUsb::readHeader(unsigned char* header, unsigned int length) if (ret == -19) // -ENODEV return false; - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } else if (ret > 0) { if ((status & COS_OnOff) == COS_OnOff) offset = 0U; @@ -230,7 +173,7 @@ bool CGMSKModemLibUsb::readHeader(unsigned char* header, unsigned int length) } if ((status & CRC_ERROR) == CRC_ERROR) { - wxLogMessage(wxT("Header - CRC Error")); + wxLogMessage("Header - CRC Error"); return false; } @@ -239,8 +182,8 @@ bool CGMSKModemLibUsb::readHeader(unsigned char* header, unsigned int length) int CGMSKModemLibUsb::readData(unsigned char* data, unsigned int length, bool& end) { - wxASSERT(data != NULL); - wxASSERT(length > 0U); + assert(data != nullptr); + assert(length > 0U); end = false; @@ -273,8 +216,8 @@ int CGMSKModemLibUsb::readData(unsigned char* data, unsigned int length, bool& e void CGMSKModemLibUsb::writeHeader(unsigned char* header, unsigned int length) { - wxASSERT(header != NULL); - wxASSERT(length >= (RADIO_HEADER_LENGTH_BYTES - 2U)); + assert(header != nullptr); + assert(length >= (RADIO_HEADER_LENGTH_BYTES - 2U)); io(0x40, SET_MyCALL2, 0, 0, (header + 35U), SHORT_CALLSIGN_LENGTH, USB_TIMEOUT); io(0x40, SET_MyCALL, 0, 0, (header + 27U), LONG_CALLSIGN_LENGTH, USB_TIMEOUT); @@ -322,8 +265,8 @@ void CGMSKModemLibUsb::setPTT(bool on) int CGMSKModemLibUsb::writeData(unsigned char* data, unsigned int length) { - wxASSERT(data != NULL); - wxASSERT(length > 0U && length <= DV_FRAME_LENGTH_BYTES); + assert(data != nullptr); + assert(length > 0U && length <= DV_FRAME_LENGTH_BYTES); if (length > GMSK_MODEM_DATA_LENGTH) { int ret = io(0x40, PUT_DATA, 0, 0, data, GMSK_MODEM_DATA_LENGTH, USB_TIMEOUT); @@ -337,7 +280,7 @@ int CGMSKModemLibUsb::writeData(unsigned char* data, unsigned int length) } // Give libUSB some recovery time - ::wxMilliSleep(3UL); + std::this_thread::sleep_for(std::chrono::milliseconds(3)); ret = io(0x40, PUT_DATA, 0, 0, (data + GMSK_MODEM_DATA_LENGTH), length - GMSK_MODEM_DATA_LENGTH, USB_TIMEOUT); if (ret < 0) { @@ -348,7 +291,7 @@ int CGMSKModemLibUsb::writeData(unsigned char* data, unsigned int length) return int(GMSK_MODEM_DATA_LENGTH); } - + return length; } else { int ret = io(0x40, PUT_DATA, 0, 0, data, length, USB_TIMEOUT); @@ -367,46 +310,19 @@ int CGMSKModemLibUsb::writeData(unsigned char* data, unsigned int length) void CGMSKModemLibUsb::close() { - wxASSERT(m_dev != NULL); + assert(m_dev != nullptr); -#if defined(WIN32) - m_usbClose(m_dev); -#else libusb_close(m_dev); -#endif - m_dev = NULL; + m_dev = nullptr; } bool CGMSKModemLibUsb::openModem() { -#if defined(WIN32) - if (!m_loaded) - return false; - - m_usbInit(); - m_usbFindBusses(); - m_usbFindDevices(); - - for (struct usb_bus* bus = m_usbGetBusses(); bus != NULL; bus = bus->next) { - for (struct usb_device* dev = bus->devices; dev != NULL; dev = dev->next) { - if (dev->descriptor.idVendor == VENDOR_ID && dev->descriptor.idProduct == m_address) { - m_dev = m_usbOpen(dev); - break; - } - } - } - - if (m_dev == NULL) - return false; - - m_usbSetConfiguration(m_dev, 1); -#else m_dev = ::libusb_open_device_with_vid_pid(m_context, VENDOR_ID, m_address); - if (m_dev == NULL) + if (m_dev == nullptr) return false; ::libusb_set_configuration(m_dev, 1); -#endif unsigned char c; io(0x40, SET_AD_INIT, 0, 0, &c, 0, USB_TIMEOUT); @@ -416,27 +332,23 @@ bool CGMSKModemLibUsb::openModem() return true; } -int CGMSKModemLibUsb::io(uint8_t requestType, uint8_t request, uint16_t value, - uint16_t index, unsigned char* data, uint16_t length, +int CGMSKModemLibUsb::io(uint8_t requestType, uint8_t request, uint16_t value, + uint16_t index, unsigned char* data, uint16_t length, unsigned int timeout) { - wxASSERT(m_dev != NULL); - wxASSERT(data != NULL); + assert(m_dev != nullptr); + assert(data != nullptr); int ret = 0; for (unsigned int i = 0U; i < 4U; i++) { -#if defined(WIN32) - ret = m_usbControlMsg(m_dev, requestType, request, value, index, data, length, timeout); -#else ret = ::libusb_control_transfer(m_dev, requestType, request, value, index, data, length, timeout); -#endif if (ret >= 0) return ret; if (ret == -19) // ENODEV return ret; - ::wxMilliSleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); } return ret; diff --git a/Common/GMSKModemLibUsb.h b/Common/GMSKModemLibUsb.h index ccc01e5..efdaf22 100644 --- a/Common/GMSKModemLibUsb.h +++ b/Common/GMSKModemLibUsb.h @@ -22,19 +22,34 @@ #include "GMSKModem.h" #include "Utils.h" -#include -#if defined(WIN32) -#include -#if _MSC_VER == 1900 -#undef __USB_H__ -#include -#else -#include "lusb0_usb.h" -#endif -#else #include -#endif +/* + * CGMSKModemLibUsb - libusb implementation of IGMSKModem for GMSK USB modems. + * + * USB vendor ID: 0x04D8 (Microchip). The address parameter is the USB product + * ID used to distinguish between multiple modems on the same host. + * + * All modem operations use USB control transfers (libusb_control_transfer). + * Request direction encoding: + * 0xC0 = device-to-host (IN): GET_VERSION, GET_HEADER, GET_DATA, + * GET_AD_STATUS, GET_REMAINSPACE + * 0x40 = host-to-device (OUT): SET_AD_INIT, SET_PTT, PUT_DATA, + * SET_MyCALL, SET_YourCALL, SET_RPT1CALL, + * SET_RPT2CALL, SET_FLAGS, SET_MyCALL2 + * + * The io() helper retries each control transfer up to 4 times on transient + * errors, returning -ENODEV (-19) immediately on device disconnect. + * + * DUTCH*Star firmware quirk (m_brokenSpace): + * Some versions report an incorrect remaining-space value from GET_REMAINSPACE. + * When detected by the firmware version string, the space check is bypassed + * by treating hasSpace() as always returning STATE_TRUE. + * + * Data is transferred in GMSK_MODEM_DATA_LENGTH chunks. Frames larger than + * one chunk are split across two sequential PUT_DATA transfers with a 3ms gap + * to give libusb recovery time between transfers. + */ class CGMSKModemLibUsb : public IGMSKModem { public: CGMSKModemLibUsb(unsigned int address); @@ -54,32 +69,15 @@ public: virtual int writeData(unsigned char* data, unsigned int length); virtual void close(); -#if defined(WIN32) - static char* (*m_usbStrerror)(); -#endif private: unsigned int m_address; -#if defined(WIN32) - struct usb_dev_handle* m_dev; - - static wxDynamicLibrary* m_library; - static bool m_loaded; - - static void (*m_usbInit)(); - static int (*m_usbFindBusses)(); - static int (*m_usbFindDevices)(); - static struct usb_bus* (*m_usbGetBusses)(); - static usb_dev_handle* (*m_usbOpen)(struct usb_device*); - static int (*m_usbSetConfiguration)(usb_dev_handle*, int); - static int (*m_usbControlMsg)(usb_dev_handle*, int, int, int, int, unsigned char*, int, int); - static int (*m_usbClose)(usb_dev_handle*); - -#else libusb_context* m_context; libusb_device_handle* m_dev; -#endif + // Executes a USB control transfer, retrying up to 4 times on transient errors. + // requestType: 0xC0 = device-to-host, 0x40 = host-to-device. + // Returns byte count on success, negative libusb error code on failure. int io(uint8_t requestType, uint8_t request, uint16_t value, uint16_t index, unsigned char* data, uint16_t length, unsigned int timeout); bool m_brokenSpace; diff --git a/Common/GMSKModemWinUSB.cpp b/Common/GMSKModemWinUSB.cpp deleted file mode 100644 index 40bc13e..0000000 --- a/Common/GMSKModemWinUSB.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) 2010-2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "GMSKModemWinUSB.h" -#include "DStarDefines.h" - -#include - -const unsigned char SET_AD_INIT = 0x00U; -const unsigned char SET_PTT = 0x05U; -const unsigned char PUT_DATA = 0x10U; -const unsigned char GET_DATA = 0x11U; -const unsigned char GET_HEADER = 0x21U; -const unsigned char GET_AD_STATUS = 0x30U; -const unsigned char SET_MyCALL = 0x40U; -const unsigned char SET_MyCALL2 = 0x41U; -const unsigned char SET_YourCALL = 0x42U; -const unsigned char SET_RPT1CALL = 0x43U; -const unsigned char SET_RPT2CALL = 0x44U; -const unsigned char SET_FLAGS = 0x45U; -const unsigned char GET_REMAINSPACE = 0x50U; -const unsigned char GET_VERSION = 0xFFU; - -const unsigned char COS_OnOff = 0x02U; -const unsigned char CRC_ERROR = 0x04U; -const unsigned char LAST_FRAME = 0x08U; -const unsigned char PTT_OnOff = 0x20U; - -const unsigned char PTT_ON = 1U; -const unsigned char PTT_OFF = 0U; - -CGMSKModemWinUSB::CGMSKModemWinUSB(unsigned int address) : -m_address(address), -m_file(INVALID_HANDLE_VALUE), -m_handle(INVALID_HANDLE_VALUE), -m_brokenSpace(false) -{ -} - -CGMSKModemWinUSB::~CGMSKModemWinUSB() -{ -} - -bool CGMSKModemWinUSB::open() -{ - wxASSERT(m_handle == INVALID_HANDLE_VALUE); - - bool res = openModem(); - if (!res) { - wxLogError(wxT("Cannot find the GMSK Modem with address: 0x%04X"), m_address); - return false; - } - - wxLogInfo(wxT("Found the GMSK Modem with address: 0x%04X"), m_address); - - wxString version; - - int ret; - do { - unsigned char buffer[GMSK_MODEM_DATA_LENGTH]; - ret = io(GET_VERSION, 0xC0U, 0U, buffer, GMSK_MODEM_DATA_LENGTH); - if (ret > 0) { - wxString text((char*)buffer, wxConvLocal, ret); - version.Append(text); - } else if (ret < 0) { - wxLogError(wxT("GET_VERSION returned %d"), -ret); - close(); - return false; - } - } while (ret == int(GMSK_MODEM_DATA_LENGTH)); - - wxLogInfo(wxT("Firmware version: %s"), version.c_str()); - - // Trap firmware version 0.1.00 of DUTCH*Star and complain loudly - if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND && version.Find(wxT("0.1.00")) != wxNOT_FOUND) { - wxLogWarning(wxT("This modem firmware is not supported by the repeater")); - wxLogWarning(wxT("Please upgrade to a newer version")); - close(); - return false; - } - - // DUTCH*Star firmware has a broken concept of free space - if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND) - m_brokenSpace = true; - - return true; -} - -bool CGMSKModemWinUSB::readHeader(unsigned char* header, unsigned int length) -{ - wxASSERT(header != NULL); - wxASSERT(length > (RADIO_HEADER_LENGTH_BYTES * 2U)); - - unsigned int offset = 0U; - - while (offset < RADIO_HEADER_LENGTH_BYTES) { - int ret = io(GET_HEADER, 0xC0U, 0U, header + offset, GMSK_MODEM_DATA_LENGTH); - if (ret < 0) { - wxLogError(wxT("GET_HEADER returned %d"), -ret); - return false; - } else if (ret == 0) { - if (offset == 0U) - return false; - - ::wxMilliSleep(10UL); - - unsigned char status; - ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U); - if (ret < 0) { - wxLogError(wxT("GET_COS returned %d"), -ret); - return false; - } else if (ret > 0) { - if ((status & COS_OnOff) == COS_OnOff) - offset = 0U; - } - } else { - offset += ret; - } - } - - unsigned char status; - int ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U); - if (ret < 0) { - wxLogError(wxT("GET_CRC returned %d"), -ret); - return false; - } - - if ((status & CRC_ERROR) == CRC_ERROR) { - wxLogMessage(wxT("Invalid CRC on header")); - return false; - } - - return true; -} - -int CGMSKModemWinUSB::readData(unsigned char* data, unsigned int length, bool& end) -{ - wxASSERT(data != NULL); - wxASSERT(length > 0U); - - end = false; - - int ret = io(GET_DATA, 0xC0U, 0U, data, GMSK_MODEM_DATA_LENGTH); - if (ret < 0) { - wxLogError(wxT("GET_DATA returned %d"), -ret); - return ret; - } else if (ret == 0) { - unsigned char status; - int ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U); - if (ret < 0) { - wxLogError(wxT("LAST_FRAME returned %d"), -ret); - return ret; - } - - if ((status & LAST_FRAME) == LAST_FRAME) - end = true; - } - - return ret; -} - -void CGMSKModemWinUSB::writeHeader(unsigned char* header, unsigned int length) -{ - wxASSERT(header != NULL); - wxASSERT(length >= (RADIO_HEADER_LENGTH_BYTES - 2U)); - - io(SET_MyCALL2, 0x40U, 0U, header + 35U, SHORT_CALLSIGN_LENGTH); - io(SET_MyCALL, 0x40U, 0U, header + 27U, LONG_CALLSIGN_LENGTH); - io(SET_YourCALL, 0x40U, 0U, header + 19U, LONG_CALLSIGN_LENGTH); - io(SET_RPT1CALL, 0x40U, 0U, header + 11U, LONG_CALLSIGN_LENGTH); - io(SET_RPT2CALL, 0x40U, 0U, header + 3U, LONG_CALLSIGN_LENGTH); - io(SET_FLAGS, 0x40U, 0U, header + 0U, 3U); -} - -TRISTATE CGMSKModemWinUSB::getPTT() -{ - unsigned char status; - int ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U); - if (ret != 1) { - wxLogError(wxT("GET_PTT returned %d"), -ret); - return STATE_UNKNOWN; - } - - if ((status & PTT_OnOff) == PTT_OnOff) - return STATE_TRUE; - else - return STATE_FALSE; -} - -void CGMSKModemWinUSB::setPTT(bool on) -{ - unsigned char c; - io(SET_PTT, 0x40U, on ? PTT_ON : PTT_OFF, &c, 0U); -} - -TRISTATE CGMSKModemWinUSB::hasSpace() -{ - unsigned char space; - int ret = io(GET_REMAINSPACE, 0xC0U, 0U, &space, 1U); - if (ret != 1) { - wxLogError(wxT("GET_REMAINSPACE returned %d"), -ret); - return STATE_UNKNOWN; - } - - if (space >= DV_FRAME_LENGTH_BYTES) - return STATE_TRUE; - else - return STATE_FALSE; -} - -int CGMSKModemWinUSB::writeData(unsigned char* data, unsigned int length) -{ - wxASSERT(data != NULL); - wxASSERT(length > 0U && length <= DV_FRAME_LENGTH_BYTES); - - if (length > GMSK_MODEM_DATA_LENGTH) { - int ret = io(PUT_DATA, 0x40U, 0U, data, GMSK_MODEM_DATA_LENGTH); - if (ret < 0) { - if (ret == -22) { - wxLogError(wxT("PUT_DATA 1, returned %d"), -ret); - return ret; - } - - return 0; - } - - ret = io(PUT_DATA, 0x40U, 0U, data + GMSK_MODEM_DATA_LENGTH, length - GMSK_MODEM_DATA_LENGTH); - if (ret < 0) { - if (ret == -22) { - wxLogError(wxT("PUT_DATA 2, returned %d"), -ret); - return ret; - } - - return int(GMSK_MODEM_DATA_LENGTH); - } - - return length; - } else { - int ret = io(PUT_DATA, 0x40U, 0U, data, length); - if (ret < 0) { - if (ret == -22) { - wxLogError(wxT("PUT_DATA returned %d"), -ret); - return ret; - } - - return 0; - } - - return length; - } -} - -void CGMSKModemWinUSB::close() -{ - wxASSERT(m_file != INVALID_HANDLE_VALUE); - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - - ::CloseHandle(m_file); - ::WinUsb_Free(m_handle); - - m_file = INVALID_HANDLE_VALUE; - m_handle = INVALID_HANDLE_VALUE; -} - -bool CGMSKModemWinUSB::openModem() -{ - WCHAR id1[15U], id2[15U]; - ::swprintf(id1, L"pid_%04x", m_address); - ::swprintf(id2, L"pid_%04X", m_address); - - wxString wxId1(id1, wxConvLocal); - wxString wxId2(id2, wxConvLocal); - - CLSID clsId; - LPOLESTR str = OLESTR("{136C76EF-3F4E-4030-A7E3-E1003EF0A715}"); - HRESULT result = ::CLSIDFromString(str, &clsId); - if (result != NOERROR) { - wxLogError(wxT("Error from CLSIDFromString: err=%lu"), ::GetLastError()); - return false; - } - - HDEVINFO devInfo = ::SetupDiGetClassDevs(&clsId, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if (devInfo == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from SetupDiGetClassDevs: err=%lu"), ::GetLastError()); - return false; - } - - SP_DEVICE_INTERFACE_DATA devInfoData; - devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - - for (unsigned int index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &clsId, index, &devInfoData); index++) { - DWORD length; - ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); - - PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); - detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - DWORD required; - BOOL ret1 = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); - if (!ret1) { - wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%lu"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - // Check the name to see if it's the correct vendor id and address - if (::wcsstr(detailData->DevicePath, id1) == NULL && ::wcsstr(detailData->DevicePath, id2) == NULL) { - ::free(detailData); - continue; - } - - m_file = ::CreateFile(detailData->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - if (m_file == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from CreateFile: err=%lu"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - ret1 = ::WinUsb_Initialize(m_file, &m_handle); - if (!ret1) { - wxLogError(wxT("Error from WinUsb_Initialize: err=%lu"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::CloseHandle(m_file); - ::free(detailData); - return false; - } - - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - - unsigned char c; - io(SET_AD_INIT, 0x40U, 0U, &c, 0U); - - setPTT(false); - - return true; - } - - ::SetupDiDestroyDeviceInfoList(devInfo); - - return false; -} - -int CGMSKModemWinUSB::io(unsigned char type, unsigned char n1, unsigned char n2, unsigned char* buffer, unsigned int length) -{ - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - - WINUSB_SETUP_PACKET packet; - packet.RequestType = n1; - packet.Request = type; - packet.Value = n2; - packet.Index = 0U; - packet.Length = length; - - ULONG transferred; - BOOL ret = ::WinUsb_ControlTransfer(m_handle, packet, buffer, length, &transferred, 0); - if (!ret) { - long error = ::GetLastError(); - return -error; - } - - return transferred; -} - diff --git a/Common/GMSKModemWinUSB.h b/Common/GMSKModemWinUSB.h deleted file mode 100644 index 789061d..0000000 --- a/Common/GMSKModemWinUSB.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2010-2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef GMSKModemWinUSB_H -#define GMSKModemWinUSB_H - -#include "GMSKModem.h" -#include "Utils.h" - -#include - -#include "winusb.h" - -class CGMSKModemWinUSB : public IGMSKModem { -public: - CGMSKModemWinUSB(unsigned int address); - virtual ~CGMSKModemWinUSB(); - - virtual bool open(); - - virtual bool readHeader(unsigned char* header, unsigned int length); - virtual int readData(unsigned char* data, unsigned int length, bool& end); - - virtual TRISTATE getPTT(); - virtual void setPTT(bool on); - - virtual TRISTATE hasSpace(); - - virtual void writeHeader(unsigned char* data, unsigned int length); - virtual int writeData(unsigned char* data, unsigned int length); - - virtual void close(); - -private: - unsigned int m_address; - HANDLE m_file; - WINUSB_INTERFACE_HANDLE m_handle; - bool m_brokenSpace; - - bool openModem(); - int io(unsigned char type, unsigned char n1, unsigned char n2, unsigned char* buffer, unsigned int length); -}; - -#endif - diff --git a/Common/GPIOController.cpp b/Common/GPIOController.cpp index 06250f0..40596ee 100644 --- a/Common/GPIOController.cpp +++ b/Common/GPIOController.cpp @@ -15,6 +15,8 @@ #include "GPIOController.h" +#include + CGPIOController::CGPIOController(unsigned int config) : m_config(config), m_outp1(false), @@ -38,7 +40,7 @@ bool CGPIOController::open() { bool ret = ::wiringPiSetup() != -1; if (!ret) { - wxLogError(wxT("Unable to initialise wiringPi")); + ::fprintf(stderr, "Unable to initialise wiringPi\n"); return false; } @@ -172,4 +174,3 @@ void CGPIOController::close() } #endif - diff --git a/Common/GPIOController.h b/Common/GPIOController.h index 75e0edd..2436a3e 100644 --- a/Common/GPIOController.h +++ b/Common/GPIOController.h @@ -18,8 +18,6 @@ #include "HardwareController.h" -#include - class CGPIOController : public IHardwareController { public: CGPIOController(unsigned int config); @@ -48,4 +46,3 @@ private: #endif #endif - diff --git a/Common/GatewayProtocolHandler.cpp b/Common/GatewayProtocolHandler.cpp index d0b11d9..f509453 100644 --- a/Common/GatewayProtocolHandler.cpp +++ b/Common/GatewayProtocolHandler.cpp @@ -21,20 +21,22 @@ #include "DStarDefines.h" #include "Utils.h" +#include + +// Uncomment to hex-dump every outbound DSRP packet to stdout for debugging. // #define DUMP_TX const unsigned int BUFFER_LENGTH = 255U; -CGatewayProtocolHandler::CGatewayProtocolHandler(const wxString& localAddress, unsigned int localPort) : +CGatewayProtocolHandler::CGatewayProtocolHandler(const std::string& localAddress, unsigned int localPort) : m_socket(localAddress, localPort), m_type(NETWORK_NONE), -m_buffer(NULL), +m_buffer(nullptr), m_length(0U) { m_buffer = new unsigned char[BUFFER_LENGTH]; - wxDateTime now = wxDateTime::UNow(); - ::srand(now.GetMillisecond()); + ::srand((unsigned int)::time(nullptr)); } CGatewayProtocolHandler::~CGatewayProtocolHandler() @@ -47,7 +49,7 @@ bool CGatewayProtocolHandler::open() return m_socket.open(); } -bool CGatewayProtocolHandler::writeHeader(const unsigned char* header, wxUint16 id, const in_addr& address, unsigned int port) +bool CGatewayProtocolHandler::writeHeader(const unsigned char* header, uint16_t id, const in_addr& address, unsigned int port) { unsigned char buffer[50U]; @@ -71,9 +73,11 @@ bool CGatewayProtocolHandler::writeHeader(const unsigned char* header, wxUint16 csum.result(buffer + 8U + RADIO_HEADER_LENGTH_BYTES - 2U); #if defined(DUMP_TX) - CUtils::dump(wxT("Sending Header"), buffer, 49U); + CUtils::dump("Sending Header", buffer, 49U); #endif + // Send the header four times to compensate for UDP packet loss. + // The repeater will deduplicate on the session ID. for (unsigned int i = 0U; i < 4U; i++) { bool ret = m_socket.write(buffer, 49U, address, port); if (!ret) @@ -83,10 +87,10 @@ bool CGatewayProtocolHandler::writeHeader(const unsigned char* header, wxUint16 return true; } -bool CGatewayProtocolHandler::writeData(const unsigned char* data, unsigned int length, wxUint16 id, wxUint8 seqNo, const in_addr& address, unsigned int port) +bool CGatewayProtocolHandler::writeData(const unsigned char* data, unsigned int length, uint16_t id, uint8_t seqNo, const in_addr& address, unsigned int port) { - wxASSERT(data != NULL); - wxASSERT(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES); + assert(data != nullptr); + assert(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES); unsigned char buffer[30U]; @@ -107,13 +111,13 @@ bool CGatewayProtocolHandler::writeData(const unsigned char* data, unsigned int ::memcpy(buffer + 9U, data, length); #if defined(DUMP_TX) - CUtils::dump(wxT("Sending Data"), buffer, length + 9U); + CUtils::dump("Sending Data", buffer, length + 9U); #endif return m_socket.write(buffer, length + 9U, address, port); } -NETWORK_TYPE CGatewayProtocolHandler::read(wxUint16& id, in_addr& address, unsigned int& port) +NETWORK_TYPE CGatewayProtocolHandler::read(uint16_t& id, in_addr& address, unsigned int& port) { bool res = true; @@ -124,7 +128,7 @@ NETWORK_TYPE CGatewayProtocolHandler::read(wxUint16& id, in_addr& address, unsig return m_type; } -bool CGatewayProtocolHandler::readPackets(wxUint16& id, in_addr& address, unsigned int& port) +bool CGatewayProtocolHandler::readPackets(uint16_t& id, in_addr& address, unsigned int& port) { m_type = NETWORK_NONE; @@ -158,7 +162,7 @@ bool CGatewayProtocolHandler::readPackets(wxUint16& id, in_addr& address, unsign } } - CUtils::dump(wxT("Unknown packet from the Repeater"), m_buffer, m_length); + CUtils::dump("Unknown packet from the Repeater", m_buffer, m_length); return true; } @@ -180,7 +184,7 @@ unsigned int CGatewayProtocolHandler::readHeader(unsigned char* buffer, unsigned bool check = csum.check(m_buffer + 8U + RADIO_HEADER_LENGTH_BYTES - 2U); if (!check) { - CUtils::dump(wxT("Header checksum failure from the Repeater"), m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES); + CUtils::dump("Header checksum failure from the Repeater", m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES); return 0U; } @@ -189,11 +193,14 @@ unsigned int CGatewayProtocolHandler::readHeader(unsigned char* buffer, unsigned return RADIO_HEADER_LENGTH_BYTES; } -unsigned int CGatewayProtocolHandler::readData(unsigned char* buffer, unsigned int length, wxUint8& seqNo, unsigned int& errors) +unsigned int CGatewayProtocolHandler::readData(unsigned char* buffer, unsigned int length, uint8_t& seqNo, unsigned int& errors) { if (m_type != NETWORK_DATA) return 0U; + if (m_length < 9U) + return 0U; + unsigned int dataLen = m_length - 9U; // Is our buffer too small? @@ -222,12 +229,13 @@ unsigned int CGatewayProtocolHandler::readData(unsigned char* buffer, unsigned i return dataLen; } -unsigned int CGatewayProtocolHandler::readRegister(wxString& name) +unsigned int CGatewayProtocolHandler::readRegister(std::string& name) { if (m_type != NETWORK_REGISTER) return 0U; - name = wxString((char*)(m_buffer + 5U), wxConvLocal); + unsigned int maxLen = (m_length > 5U) ? (m_length - 5U) : 0U; + name = std::string((char*)(m_buffer + 5U), ::strnlen((char*)(m_buffer + 5U), maxLen)); return m_length - 6U; } diff --git a/Common/GatewayProtocolHandler.h b/Common/GatewayProtocolHandler.h index 107080e..2203e9e 100644 --- a/Common/GatewayProtocolHandler.h +++ b/Common/GatewayProtocolHandler.h @@ -21,24 +21,38 @@ #include "UDPReaderWriter.h" #include "DStarDefines.h" +#include "StdCompat.h" -#include -#include - +/* + * Implements the D-Star Repeater Protocol (DSRP) from the gateway side. + * + * This is the mirror of CRepeaterProtocolHandler — it is used by the gateway + * process (ircDDBGateway/DStarGateway) to communicate with one or more + * repeater daemons. Unlike the repeater side, there is no fixed peer address: + * each packet carries the sender's address and port, which are passed through + * to the caller so it can route replies back to the correct repeater. + * + * The same DSRP packet types are used as on the repeater side (0x20 header, + * 0x21 data, 0x0B register). The key difference is that the gateway sends + * headers four times for redundancy (vs two on the repeater side). + */ class CGatewayProtocolHandler { public: - CGatewayProtocolHandler(const wxString& localAddress, unsigned int localPort); + CGatewayProtocolHandler(const std::string& localAddress, unsigned int localPort); ~CGatewayProtocolHandler(); bool open(); - bool writeHeader(const unsigned char* header, wxUint16 id, const in_addr& address, unsigned int port); - bool writeData(const unsigned char* data, unsigned int length, wxUint16 id, wxUint8 seqNo, const in_addr& address, unsigned int port); + // Sends the header packet four times to reduce the impact of UDP packet loss. + bool writeHeader(const unsigned char* header, uint16_t id, const in_addr& address, unsigned int port); + bool writeData(const unsigned char* data, unsigned int length, uint16_t id, uint8_t seqNo, const in_addr& address, unsigned int port); - NETWORK_TYPE read(wxUint16& id, in_addr& address, unsigned int& port); + // Drains pending datagrams; fills address/port with the sender for routing. + NETWORK_TYPE read(uint16_t& id, in_addr& address, unsigned int& port); unsigned int readHeader(unsigned char* data, unsigned int length); - unsigned int readData(unsigned char* data, unsigned int length, wxUint8& seqNo, unsigned int& errors); - unsigned int readRegister(wxString& name); + unsigned int readData(unsigned char* data, unsigned int length, uint8_t& seqNo, unsigned int& errors); + // Reads a Register packet; name is the repeater's self-reported name string. + unsigned int readRegister(std::string& name); void close(); @@ -48,7 +62,7 @@ private: unsigned char* m_buffer; unsigned int m_length; - bool readPackets(wxUint16& id, in_addr& address, unsigned int& port); + bool readPackets(uint16_t& id, in_addr& address, unsigned int& port); }; #endif diff --git a/Common/Golay.h b/Common/Golay.h index eb3d65f..d68ccef 100644 --- a/Common/Golay.h +++ b/Common/Golay.h @@ -19,6 +19,16 @@ #ifndef Golay_H #define Golay_H +/* + * Golay error-correcting code used by the AMBE FEC layer. + * + * Provides two variants: + * Golay(23,12) — encodes 12 data bits into 23 bits; corrects up to 3 errors. + * Golay(24,12) — adds a parity bit to the above for even-parity detection. + * + * encode*() returns the full codeword (data + parity bits). + * decode*() returns the corrected 12-bit data word, or ~0U on uncorrectable error. + */ class CGolay { public: static unsigned int encode23127(unsigned int data); diff --git a/Common/HardwareController.h b/Common/HardwareController.h index 0035f84..b952c3b 100644 --- a/Common/HardwareController.h +++ b/Common/HardwareController.h @@ -14,6 +14,18 @@ #ifndef HardwareController_H #define HardwareController_H +/* + * Abstract interface for hardware I/O controllers. + * + * Concrete implementations (CSerialLineController, CGPIOController, + * CArduinoController, CK8055Controller, etc.) allow the repeater to + * drive external PTT lines, squelch outputs, and read COR/COS inputs + * through whatever physical interface is attached to the host machine. + * + * Up to five digital inputs and eight digital outputs are supported; + * the meaning of each pin is determined by the configured controller + * type and the hardware wiring. + */ class IHardwareController { public: virtual ~IHardwareController() = 0; diff --git a/Common/HeaderData.cpp b/Common/HeaderData.cpp index 7f612af..68c558c 100644 --- a/Common/HeaderData.cpp +++ b/Common/HeaderData.cpp @@ -20,8 +20,11 @@ #include "HeaderData.h" #include "DStarDefines.h" +#include +#include + CHeaderData::CHeaderData() : -m_time(), +m_time(0), m_myCall1(), m_myCall2(), m_yourCall(), @@ -49,7 +52,7 @@ m_valid(header.m_valid) } CHeaderData::CHeaderData(const unsigned char* data, unsigned int length, bool check) : -m_time(), +m_time(0), m_myCall1(), m_myCall2(), m_yourCall(), @@ -60,29 +63,29 @@ m_flag2(0x00), m_flag3(0x00), m_valid(true) { - wxASSERT(data != NULL); - wxASSERT(length >= (RADIO_HEADER_LENGTH_BYTES - 2U)); + assert(data != nullptr); + assert(length >= (RADIO_HEADER_LENGTH_BYTES - 2U)); const unsigned char* p = data; m_flag1 = *p++; m_flag2 = *p++; m_flag3 = *p++; - m_rptCall2 = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + m_rptCall2 = std::string((const char*)p, LONG_CALLSIGN_LENGTH); p += LONG_CALLSIGN_LENGTH; - m_rptCall1 = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + m_rptCall1 = std::string((const char*)p, LONG_CALLSIGN_LENGTH); p += LONG_CALLSIGN_LENGTH; - m_yourCall = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + m_yourCall = std::string((const char*)p, LONG_CALLSIGN_LENGTH); p += LONG_CALLSIGN_LENGTH; - m_myCall1 = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + m_myCall1 = std::string((const char*)p, LONG_CALLSIGN_LENGTH); p += LONG_CALLSIGN_LENGTH; - m_myCall2 = wxString((const char*)p, wxConvLocal, SHORT_CALLSIGN_LENGTH); + m_myCall2 = std::string((const char*)p, SHORT_CALLSIGN_LENGTH); - m_time.SetToCurrent(); + m_time = ::time(nullptr); // We have a checksum, check it if asked if (length >= RADIO_HEADER_LENGTH_BYTES && check) { @@ -94,10 +97,10 @@ m_valid(true) } } -CHeaderData::CHeaderData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall, - const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1, +CHeaderData::CHeaderData(const std::string& myCall1, const std::string& myCall2, const std::string& yourCall, + const std::string& rptCall1, const std::string& rptCall2, unsigned char flag1, unsigned char flag2, unsigned char flag3) : -m_time(), +m_time(0), m_myCall1(myCall1), m_myCall2(myCall2), m_yourCall(yourCall), @@ -108,51 +111,51 @@ m_flag2(flag2), m_flag3(flag3), m_valid(true) { - m_time.SetToCurrent(); + m_time = ::time(nullptr); - m_myCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_myCall2.Append(wxT(' '), SHORT_CALLSIGN_LENGTH); - m_yourCall.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_rptCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_rptCall2.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_myCall1.resize(LONG_CALLSIGN_LENGTH, ' '); + m_myCall2.resize(SHORT_CALLSIGN_LENGTH, ' '); + m_yourCall.resize(LONG_CALLSIGN_LENGTH, ' '); + m_rptCall1.resize(LONG_CALLSIGN_LENGTH, ' '); + m_rptCall2.resize(LONG_CALLSIGN_LENGTH, ' '); - m_myCall1.Truncate(LONG_CALLSIGN_LENGTH); - m_myCall2.Truncate(SHORT_CALLSIGN_LENGTH); - m_yourCall.Truncate(LONG_CALLSIGN_LENGTH); - m_rptCall1.Truncate(LONG_CALLSIGN_LENGTH); - m_rptCall2.Truncate(LONG_CALLSIGN_LENGTH); + m_myCall1.resize(LONG_CALLSIGN_LENGTH); + m_myCall2.resize(SHORT_CALLSIGN_LENGTH); + m_yourCall.resize(LONG_CALLSIGN_LENGTH); + m_rptCall1.resize(LONG_CALLSIGN_LENGTH); + m_rptCall2.resize(LONG_CALLSIGN_LENGTH); } CHeaderData::~CHeaderData() { } -wxDateTime CHeaderData::getTime() const +time_t CHeaderData::getTime() const { return m_time; } -wxString CHeaderData::getMyCall1() const +std::string CHeaderData::getMyCall1() const { return m_myCall1; } -wxString CHeaderData::getMyCall2() const +std::string CHeaderData::getMyCall2() const { return m_myCall2; } -wxString CHeaderData::getYourCall() const +std::string CHeaderData::getYourCall() const { return m_yourCall; } -wxString CHeaderData::getRptCall1() const +std::string CHeaderData::getRptCall1() const { return m_rptCall1; } -wxString CHeaderData::getRptCall2() const +std::string CHeaderData::getRptCall2() const { return m_rptCall2; } @@ -172,39 +175,39 @@ unsigned char CHeaderData::getFlag3() const return m_flag3; } -void CHeaderData::setMyCall1(const wxString& callsign) +void CHeaderData::setMyCall1(const std::string& callsign) { m_myCall1 = callsign; - m_myCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_myCall1.Truncate(LONG_CALLSIGN_LENGTH); + m_myCall1.resize(LONG_CALLSIGN_LENGTH, ' '); + m_myCall1.resize(LONG_CALLSIGN_LENGTH); } -void CHeaderData::setMyCall2(const wxString& callsign) +void CHeaderData::setMyCall2(const std::string& callsign) { m_myCall2 = callsign; - m_myCall2.Append(wxT(' '), SHORT_CALLSIGN_LENGTH); - m_myCall2.Truncate(SHORT_CALLSIGN_LENGTH); + m_myCall2.resize(SHORT_CALLSIGN_LENGTH, ' '); + m_myCall2.resize(SHORT_CALLSIGN_LENGTH); } -void CHeaderData::setYourCall(const wxString& callsign) +void CHeaderData::setYourCall(const std::string& callsign) { m_yourCall = callsign; - m_yourCall.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_yourCall.Truncate(LONG_CALLSIGN_LENGTH); + m_yourCall.resize(LONG_CALLSIGN_LENGTH, ' '); + m_yourCall.resize(LONG_CALLSIGN_LENGTH); } -void CHeaderData::setRptCall1(const wxString& callsign) +void CHeaderData::setRptCall1(const std::string& callsign) { m_rptCall1 = callsign; - m_rptCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_rptCall1.Truncate(LONG_CALLSIGN_LENGTH); + m_rptCall1.resize(LONG_CALLSIGN_LENGTH, ' '); + m_rptCall1.resize(LONG_CALLSIGN_LENGTH); } -void CHeaderData::setRptCall2(const wxString& callsign) +void CHeaderData::setRptCall2(const std::string& callsign) { m_rptCall2 = callsign; - m_rptCall2.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_rptCall2.Truncate(LONG_CALLSIGN_LENGTH); + m_rptCall2.resize(LONG_CALLSIGN_LENGTH, ' '); + m_rptCall2.resize(LONG_CALLSIGN_LENGTH); } bool CHeaderData::isAck() const @@ -320,11 +323,11 @@ bool CHeaderData::isValid() const void CHeaderData::reset() { - m_myCall1 = wxT(" "); - m_myCall2 = wxT(" "); - m_yourCall = wxT("CQCQCQ "); - m_rptCall1 = wxT("DIRECT "); - m_rptCall2 = wxT("DIRECT "); + m_myCall1 = " "; + m_myCall2 = " "; + m_yourCall = "CQCQCQ "; + m_rptCall1 = "DIRECT "; + m_rptCall2 = "DIRECT "; m_flag1 = 0x00; m_flag2 = 0x00; diff --git a/Common/HeaderData.h b/Common/HeaderData.h index 69207dd..66f0547 100644 --- a/Common/HeaderData.h +++ b/Common/HeaderData.h @@ -19,25 +19,52 @@ #ifndef HeaderData_H #define HeaderData_H -#include -#include +#include +#include +/* + * Represents a D-Star radio header — the block transmitted before every voice + * or data transmission that identifies the participants and routing. + * + * Wire format (41 bytes, all callsigns space-padded to fixed width): + * [0] Flag 1 — repeater/data/control/urgent/BK/EMR bits + * [1] Flag 2 — reserved (always 0x00 in practice) + * [2] Flag 3 — reserved (always 0x00 in practice) + * [3..10] RPT2 — outgoing repeater callsign (8 chars, LONG_CALLSIGN_LENGTH) + * [11..18] RPT1 — incoming repeater callsign (8 chars) + * [19..26] YOUR — destination callsign or "CQCQCQ " for a general call + * [27..34] MY1 — originating station callsign (8 chars) + * [35..38] MY2 — 4-character callsign suffix (SHORT_CALLSIGN_LENGTH) + * [39..40] Checksum — CCITT-16 (reversed bit order) over bytes [0..38] + * + * The checksum is computed by CCCITTChecksumReverse. A stored checksum of + * 0xFFFF is treated as "unchecked" and is accepted without verification; this + * is used when the header arrives from a trusted local source. + * + * Flag 1 bit meanings (see DStarDefines.h for mask constants): + * Bit 6 Repeater mode — set when relayed through a repeater + * Bit 5 Data packet — DV data vs. voice + * Bit 4 Interrupted — previous transmission was interrupted (BK) + * Bit 2 Control signal — auto-control frame + * Bit 0 Urgent — emergency (EMR) + */ class CHeaderData { public: CHeaderData(); CHeaderData(const CHeaderData& header); + // Deserialise from raw wire bytes; set check=true to validate the checksum. CHeaderData(const unsigned char* data, unsigned int length, bool check); - CHeaderData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall, - const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1 = 0x00, + CHeaderData(const std::string& myCall1, const std::string& myCall2, const std::string& yourCall, + const std::string& rptCall1, const std::string& rptCall2, unsigned char flag1 = 0x00, unsigned char flag2 = 0x00, unsigned char flag3 = 0x00); ~CHeaderData(); - wxDateTime getTime() const; - wxString getMyCall1() const; - wxString getMyCall2() const; - wxString getYourCall() const; - wxString getRptCall1() const; - wxString getRptCall2() const; + time_t getTime() const; + std::string getMyCall1() const; + std::string getMyCall2() const; + std::string getYourCall() const; + std::string getRptCall1() const; + std::string getRptCall2() const; unsigned char getFlag1() const; unsigned char getFlag2() const; @@ -57,11 +84,11 @@ public: void setFlag2(unsigned char flag); void setFlag3(unsigned char flag); - void setMyCall1(const wxString& callsign); - void setMyCall2(const wxString& callsign); - void setYourCall(const wxString& callsign); - void setRptCall1(const wxString& callsign); - void setRptCall2(const wxString& callsign); + void setMyCall1(const std::string& callsign); + void setMyCall2(const std::string& callsign); + void setYourCall(const std::string& callsign); + void setRptCall1(const std::string& callsign); + void setRptCall2(const std::string& callsign); void setRepeaterMode(bool set); void setDataPacket(bool set); @@ -70,6 +97,7 @@ public: void setUrgent(bool set); void setRepeaterFlags(unsigned char set); + // Returns false if the checksum was checked and failed during deserialisation. bool isValid() const; void reset(); @@ -77,16 +105,16 @@ public: CHeaderData& operator=(const CHeaderData& header); private: - wxDateTime m_time; - wxString m_myCall1; - wxString m_myCall2; - wxString m_yourCall; - wxString m_rptCall1; - wxString m_rptCall2; + time_t m_time; + std::string m_myCall1; + std::string m_myCall2; + std::string m_yourCall; + std::string m_rptCall1; + std::string m_rptCall2; unsigned char m_flag1; unsigned char m_flag2; unsigned char m_flag3; - bool m_valid; + bool m_valid; // Set to false if checksum validation failed. }; #endif diff --git a/Common/IcomController.cpp b/Common/IcomController.cpp index 5c1ed10..f138d31 100644 --- a/Common/IcomController.cpp +++ b/Common/IcomController.cpp @@ -20,12 +20,12 @@ #include "DStarDefines.h" #include "Timer.h" #include "Utils.h" +#include "Logger.h" -#if defined(__WINDOWS__) -#include -#else -#include -#endif +#include +#include +#include +#include enum STATE_ICOM { SI_NONE, @@ -37,7 +37,7 @@ const unsigned int MAX_RESPONSES = 30U; const unsigned int BUFFER_LENGTH = 200U; -CIcomController::CIcomController(const wxString& port) : +CIcomController::CIcomController(const std::string& port) : CModem(), m_port(port), m_serial(port, SERIAL_38400, true), @@ -45,7 +45,7 @@ m_txData(2000U), m_txCounter(0U), m_pktCounter(0U) { - wxASSERT(!port.IsEmpty()); + assert(!port.empty()); } CIcomController::~CIcomController() @@ -58,16 +58,14 @@ bool CIcomController::start() if (!ret) return false; - Create(); - SetPriority(100U); - Run(); + m_thread = std::thread(&CIcomController::entry, this); return true; } -void* CIcomController::Entry() +void CIcomController::entry() { - wxLogMessage(wxT("Starting Icom Controller thread")); + wxLogMessage("Starting Icom Controller thread"); // Clock every 5ms-ish CTimer pollTimer(200U, 0U, 100U); @@ -77,14 +75,14 @@ void* CIcomController::Entry() CTimer lostTimer(200U, 5U); lostTimer.start(); - + unsigned char storeData[BUFFER_LENGTH]; unsigned int storeLength = 0U; - + unsigned int pollCount = 0U; - + bool connected = false; - + STATE_ICOM state = SI_NONE; unsigned char seqNo = 0U; bool txSpace = true; @@ -113,13 +111,13 @@ void* CIcomController::Entry() break; case RTI_ERROR: - wxLogMessage(wxT("Stopping Icom Controller thread")); - return NULL; + wxLogMessage("Stopping Icom Controller thread"); + return; case RTI_HEADER: { - // CUtils::dump(wxT("RTI_HEADER"), buffer, length); + // CUtils::dump("RTI_HEADER", buffer, length); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_HEADER; @@ -134,9 +132,9 @@ void* CIcomController::Entry() break; case RTI_DATA: { - // CUtils::dump(wxT("RTI_DATA"), buffer, length); + // CUtils::dump("RTI_DATA", buffer, length); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_DATA; @@ -151,9 +149,9 @@ void* CIcomController::Entry() break; case RTI_EOT: { - // wxLogMessage(wxT("RTI_EOT")); + // wxLogMessage("RTI_EOT"); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; @@ -166,23 +164,23 @@ void* CIcomController::Entry() break; case RTI_PONG: - // wxLogMessage(wxT("RTI_PONG")); + // wxLogMessage("RTI_PONG"); if (!connected) - wxLogMessage(wxT("Connected to the Icom radio")); + wxLogMessage("Connected to the Icom radio"); lostTimer.start(); connected = true; break; case RTI_HEADER_ACK: if (buffer[2U] == 0x00U) { - // wxLogMessage(wxT("RTI_HEADER_ACK")); + // wxLogMessage("RTI_HEADER_ACK"); if (state == SI_HEADER) { storeLength = 0U; retryTimer.stop(); txSpace = true; } } else { - wxLogMessage(wxT("RTI_HEADER_NAK")); + wxLogMessage("RTI_HEADER_NAK"); } lostTimer.start(); @@ -190,33 +188,33 @@ void* CIcomController::Entry() case RTI_DATA_ACK: if (buffer[3U] == 0x00U) { - // wxLogMessage(wxT("RTI_DATA_ACK - %02X"), buffer[2U]); + // wxLogMessage("RTI_DATA_ACK - %02X", buffer[2U]); if (state == SI_DATA && seqNo == buffer[2U]) { storeLength = 0U; retryTimer.stop(); txSpace = true; } } else { - wxLogMessage(wxT("RTI_DATA_NAK - %02X"), buffer[2U]); + wxLogMessage("RTI_DATA_NAK - %02X", buffer[2U]); } lostTimer.start(); break; default: - wxLogMessage(wxT("Unknown message, type: %02X"), buffer[1U]); - CUtils::dump(wxT("Buffer dump"), buffer, length); + wxLogMessage("Unknown message, type: %02X", buffer[1U]); + CUtils::dump("Buffer dump", buffer, length); break; } - + if (retryTimer.isRunning() && retryTimer.hasExpired()) { assert(storeLength > 0U); - // CUtils::dump(wxT("Re-Sending"), storeData, storeLength + 1U); + // CUtils::dump("Re-Sending", storeData, storeLength + 1U); int ret = m_serial.write(storeData, storeLength + 1U); if (ret != int(storeLength + 1U)) - wxLogWarning(wxT("Error when writing to the Icom radio")); + wxLogWarning("Error when writing to the Icom radio"); retryTimer.start(); pollTimer.start(); @@ -228,7 +226,7 @@ void* CIcomController::Entry() storeLength = storeData[0U]; m_txData.getData(storeData + 1U, storeLength); - // CUtils::dump(wxT("Sending"), storeData, storeLength + 1U); + // CUtils::dump("Sending", storeData, storeLength + 1U); if (storeData[1U] == 0x20U) { state = SI_HEADER; @@ -240,7 +238,7 @@ void* CIcomController::Entry() int ret = m_serial.write(storeData, storeLength + 1U); if (ret != int(storeLength + 1U)) - wxLogWarning(wxT("Error when writing to the Icom radio")); + wxLogWarning("Error when writing to the Icom radio"); retryTimer.start(); pollTimer.start(); @@ -248,15 +246,15 @@ void* CIcomController::Entry() txSpace = false; } - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); pollTimer.clock(); lostTimer.clock(); retryTimer.clock(); - + if (lostTimer.hasExpired()) { if (connected) - wxLogWarning(wxT("Lost connection to the Icom radio")); + wxLogWarning("Lost connection to the Icom radio"); pollTimer.setTimeout(0U, 100U); pollTimer.start(); @@ -276,16 +274,14 @@ void* CIcomController::Entry() m_serial.close(); - wxLogMessage(wxT("Stopping Icom Controller thread")); - - return NULL; + wxLogMessage("Stopping Icom Controller thread"); } bool CIcomController::writeHeader(const CHeaderData& header) { bool ret = m_txData.hasSpace(43U); if (!ret) { - wxLogWarning(wxT("No space to write the header")); + wxLogWarning("No space to write the header"); return false; } @@ -301,47 +297,52 @@ bool CIcomController::writeHeader(const CHeaderData& header) buffer[3U] = header.getFlag2(); buffer[4U] = header.getFlag3(); - wxString rpt2 = header.getRptCall2(); - for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 5U] = rpt2.GetChar(i); + std::string rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 5U] = rpt2[i]; - wxString rpt1 = header.getRptCall1(); - for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 13U] = rpt1.GetChar(i); + std::string rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 13U] = rpt1[i]; - wxString your = header.getYourCall(); - for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 21U] = your.GetChar(i); + std::string your = header.getYourCall(); + for (unsigned int i = 0U; i < your.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 21U] = your[i]; - wxString my1 = header.getMyCall1(); - for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 29U] = my1.GetChar(i); + std::string my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 29U] = my1[i]; - wxString my2 = header.getMyCall2(); - for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) - buffer[i + 37U] = my2.GetChar(i); + std::string my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.size() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 37U] = my2[i]; buffer[41U] = 0xFFU; m_txCounter = 0U; m_pktCounter = 0U; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); m_txData.addData(buffer, 42U); return true; } +// Builds an Icom data packet (type 0x22). +// m_txCounter is the stream sequence (increments per packet, never wraps to 0). +// m_pktCounter counts frames within a super-frame (0-20), resetting either when +// a data-sync pattern is detected in the AMBE payload or at 21 frames. +// The end-of-transmission flag is bit 6 of the pktCounter byte (0x40). bool CIcomController::writeData(const unsigned char* data, unsigned int, bool end) { bool ret = m_txData.hasSpace(18U); if (!ret) { - wxLogWarning(wxT("No space to write data")); + wxLogWarning("No space to write data"); return false; } - unsigned char buffer[20U]; + unsigned char buffer[20U] = {}; if (end) { buffer[0U] = 0x10U; @@ -355,7 +356,7 @@ bool CIcomController::writeData(const unsigned char* data, unsigned int, bool en buffer[16U] = 0xFFU; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); m_txData.addData(buffer, 17U); @@ -371,14 +372,14 @@ bool CIcomController::writeData(const unsigned char* data, unsigned int, bool en m_txCounter++; m_pktCounter++; - if (::memcmp(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES) == 0 || m_pktCounter == 21U) + if (::memcmp(data + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES) == 0 || m_pktCounter == 21U) m_pktCounter = 0U; ::memcpy(buffer + 4U, data, DV_FRAME_LENGTH_BYTES); buffer[16U] = 0xFFU; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); m_txData.addData(buffer, 17U); @@ -395,17 +396,23 @@ bool CIcomController::isTXReady() return true; } -wxString CIcomController::getPath() const +std::string CIcomController::getPath() const { - return wxEmptyString; + return std::string(); } +// Reads one Icom serial frame. +// byte[0] = length of the remaining payload (valid values: 0x03, 0x04, 0x10, 0x2C). +// byte[1] = message type (0x03 pong, 0x10 header, 0x12 data/EOT, 0x21 hdr-ack, 0x23 data-ack). +// A value of 0xFF in byte[0] is a line-idle fill byte; treated as RT_TIMEOUT. +// All reads on bytes[2..] use a 40ms timeout to avoid blocking the main loop +// indefinitely if the radio stops mid-frame. RESP_TYPE_ICOM CIcomController::getResponse(unsigned char *buffer, unsigned int& length) { // Get the start of the frame or nothing at all int ret = m_serial.read(buffer, 1U); if (ret < 0) { - wxLogError(wxT("Error when reading the length from the Icom radio")); + wxLogError("Error when reading the length from the Icom radio"); return RTI_ERROR; } @@ -419,13 +426,13 @@ RESP_TYPE_ICOM CIcomController::getResponse(unsigned char *buffer, unsigned int& // Validate the message lengths if (buffer[0U] != 0x03U && buffer[0U] != 0x04U && buffer[0U] != 0x10U && buffer[0U] != 0x2CU) { - wxLogError(wxT("Invalid data length received from the Icom radio - 0x%02X"), length); + wxLogError("Invalid data length received from the Icom radio - 0x%02X", length); return RTI_TIMEOUT; } ret = m_serial.read(buffer + 1U, 1U, 40U); if (ret < 0) { - wxLogError(wxT("Error when reading the type from the Icom radio")); + wxLogError("Error when reading the type from the Icom radio"); return RTI_ERROR; } @@ -434,7 +441,7 @@ RESP_TYPE_ICOM CIcomController::getResponse(unsigned char *buffer, unsigned int& // Validate the message types if (buffer[1U] != 0x03U && buffer[1U] != 0x10U && buffer[1U] != 0x12U && buffer[1U] != 0x21U && buffer[1U] != 0x23U) { - wxLogError(wxT("Invalid data type received from the Icom radio - 0x%02X"), buffer[1U]); + wxLogError("Invalid data type received from the Icom radio - 0x%02X", buffer[1U]); return RTI_TIMEOUT; } @@ -443,7 +450,7 @@ RESP_TYPE_ICOM CIcomController::getResponse(unsigned char *buffer, unsigned int& while (offset < length) { ret = m_serial.read(buffer + offset, length - offset, 40U); if (ret < 0) { - wxLogError(wxT("Error when reading data from the Icom radio")); + wxLogError("Error when reading data from the Icom radio"); return RTI_ERROR; } @@ -451,12 +458,12 @@ RESP_TYPE_ICOM CIcomController::getResponse(unsigned char *buffer, unsigned int& offset += ret; if (ret == 0) { - // CUtils::dump(wxT("Receive timed out"), buffer, offset); + // CUtils::dump("Receive timed out", buffer, offset); return RTI_TIMEOUT; } } - // CUtils::dump(wxT("Received"), buffer, length); + // CUtils::dump("Received", buffer, length); switch (buffer[1U]) { case 0x03U: diff --git a/Common/IcomController.h b/Common/IcomController.h index d9f15bd..37b2e79 100644 --- a/Common/IcomController.h +++ b/Common/IcomController.h @@ -23,8 +23,9 @@ #include "RingBuffer.h" #include "Modem.h" #include "Utils.h" +#include "StdCompat.h" -#include +#include enum RESP_TYPE_ICOM { RTI_TIMEOUT, @@ -38,13 +39,46 @@ enum RESP_TYPE_ICOM { RTI_PONG }; +/* + * CIcomController - Driver for Icom radios in terminal/access-point mode. + * + * Hardware interface: Serial at 38400 baud with RTS/CTS flow control. + * + * Protocol: Length-prefixed binary frames. The first byte of each frame is + * the payload length; valid lengths are 0x03, 0x04, 0x10, and 0x2C. + * The second byte is the message type: + * 0x03 - PONG (keepalive response) + * 0x10 - D-Star header received (44 bytes payload) + * 0x12 - D-Star data frame received (12 bytes payload) + * bit 6 of byte[3] set = end-of-transmission + * 0x21 - Header ACK/NAK (byte[2] == 0x00 is ACK) + * 0x23 - Data ACK/NAK (byte[2] = sequence, byte[3] == 0x00 is ACK) + * + * Packet counters: + * m_txCounter - monotonically incrementing TX stream ID (rolls at 255 -> 1). + * m_pktCounter - per-packet sequence within a stream, resets on data-sync + * pattern (0x55 0x2D 0x16) or at 21. The radio uses this to + * detect lost frames and request retransmission. + * + * Connection management: + * The driver sends poll frames (0xFF 0xFF 0xFF) initially and ping frames + * (0x02 0x02 0xFF) after the 18th poll. A 5-second lostTimer triggers + * reconnection if no response (PONG or data) is received. + * + * ACK/retry: + * Each transmitted packet is held in storeData until the matching ACK is + * received (matched by sequence number for data frames). A 50ms retryTimer + * re-sends the packet if no ACK arrives. txSpace gates the send queue to + * ensure only one unacknowledged packet is in flight at a time. + * + * getPath() returns an empty string - Icom radios do not use the sysfs USB + * path tracking used by the MMDVM/DV-RPTR family. + */ class CIcomController : public CModem { public: - CIcomController(const wxString& port); + CIcomController(const std::string& port); virtual ~CIcomController(); - virtual void* Entry(); - virtual bool start(); virtual unsigned int getSpace(); @@ -53,19 +87,22 @@ public: virtual bool writeHeader(const CHeaderData& header); virtual bool writeData(const unsigned char* data, unsigned int length, bool end); - virtual wxString getPath() const; + virtual std::string getPath() const; private: - wxString m_port; + void entry(); + + std::string m_port; CSerialDataController m_serial; CRingBuffer m_txData; unsigned char m_txCounter; unsigned char m_pktCounter; RESP_TYPE_ICOM getResponse(unsigned char* buffer, unsigned int& length); + // Sends 0xFF 0xFF 0xFF - null keepalive before the radio responds to pings. bool writePoll(); + // Sends 0x02 0x02 0xFF - ping request; elicits a PONG response from the radio. bool writePing(); }; #endif - diff --git a/Common/K8055Controller.cpp b/Common/K8055Controller.cpp index 6cbe8eb..b1158cb 100644 --- a/Common/K8055Controller.cpp +++ b/Common/K8055Controller.cpp @@ -13,6 +13,11 @@ #include "K8055Controller.h" +#include +#include + +#if !defined(_WIN32) + const unsigned int VELLEMAN_VENDOR_ID = 0x10CFU; const unsigned int VELLEMAN_PRODUCT_ID = 0x5500U; @@ -36,208 +41,6 @@ const char OUT_PORT6 = 0x20U; const char OUT_PORT7 = 0x40U; const char OUT_PORT8 = 0x80U; -#if defined(__WINDOWS__) - -#include -#include - -const DWORD USB_BUFSIZE = 9UL; - -CK8055Controller::CK8055Controller(unsigned int address) : -m_address(address), -m_outp1(false), -m_outp2(false), -m_outp3(false), -m_outp4(false), -m_outp5(false), -m_outp6(false), -m_outp7(false), -m_outp8(false), -m_handle(INVALID_HANDLE_VALUE) -{ -} - -CK8055Controller::~CK8055Controller() -{ -} - -bool CK8055Controller::open() -{ - wxASSERT(m_handle == INVALID_HANDLE_VALUE); - - GUID guid; - ::HidD_GetHidGuid(&guid); - - HDEVINFO devInfo = ::SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); - if (devInfo == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); - return false; - } - - SP_DEVICE_INTERFACE_DATA devInfoData; - devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - - for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guid, index, &devInfoData); index++) { - // Find the required length of the device structure - DWORD length; - ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); - - PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); - detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - // Get the detailed data into the newly allocated device structure - DWORD required; - BOOL res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); - if (!res) { - wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - // Get the handle for getting the attributes - HANDLE handle = ::CreateFile(detailData->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (handle == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from CreateFile: err=%u"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - HIDD_ATTRIBUTES attributes; - attributes.Size = sizeof(HIDD_ATTRIBUTES); - res = ::HidD_GetAttributes(handle, &attributes); - if (!res) { - wxLogError(wxT("Error from HidD_GetAttributes: err=%u"), ::GetLastError()); - ::CloseHandle(handle); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - ::CloseHandle(handle); - - // Is this a Velleman K8055 and the right one? - if (attributes.VendorID == VELLEMAN_VENDOR_ID && attributes.ProductID == (VELLEMAN_PRODUCT_ID + m_address)) { - m_handle = ::CreateFile(detailData->DevicePath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (m_handle == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from CreateFile: err=%u"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - - setDigitalOutputs(false, false, false, false, false, false, false, false); - - return true; - } - - ::free(detailData); - } - - ::SetupDiDestroyDeviceInfoList(devInfo); - - return false; -} - -void CK8055Controller::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) -{ - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - - char buffer[USB_BUFSIZE]; - buffer[0] = REPORT_ID; - buffer[1] = 0x00U; - buffer[2] = 0x00U; - buffer[3] = 0x00U; - buffer[4] = 0x00U; - buffer[5] = 0x00U; - buffer[6] = 0x00U; - buffer[7] = 0x00U; - buffer[8] = 0x00U; - - DWORD read; - BOOL res = ::ReadFile(m_handle, buffer, USB_BUFSIZE, &read, NULL); - if (!res) { - wxLogError(wxT("Error from ReadFile: err=%u, read=%u"), ::GetLastError(), read); - return; - } - - // Do we have data? - if (buffer[2] != 0x00) { - inp1 = (buffer[1] & IN_PORT1) == IN_PORT1; - inp2 = (buffer[1] & IN_PORT2) == IN_PORT2; - inp3 = (buffer[1] & IN_PORT3) == IN_PORT3; - inp4 = (buffer[1] & IN_PORT4) == IN_PORT4; - inp5 = (buffer[1] & IN_PORT5) == IN_PORT5; - } -} - -void CK8055Controller::setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8) -{ - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - - if (outp1 == m_outp1 && outp2 == m_outp2 && outp3 == m_outp3 && outp4 == m_outp4 && - outp5 == m_outp5 && outp6 == m_outp6 && outp7 == m_outp7 && outp8 == m_outp8) - return; - - char buffer[USB_BUFSIZE]; - buffer[0] = REPORT_ID; - buffer[1] = CMD_SET_ANALOG_DIGITAL; - buffer[2] = 0x00U; - buffer[3] = 0x00U; - buffer[4] = 0x00U; - buffer[5] = 0x00U; - buffer[6] = 0x00U; - buffer[7] = 0x00U; - buffer[8] = 0x00U; - - if (outp1) - buffer[2] |= OUT_PORT1; - if (outp2) - buffer[2] |= OUT_PORT2; - if (outp3) - buffer[2] |= OUT_PORT3; - if (outp4) - buffer[2] |= OUT_PORT4; - if (outp5) - buffer[2] |= OUT_PORT5; - if (outp6) - buffer[2] |= OUT_PORT6; - if (outp7) - buffer[2] |= OUT_PORT7; - if (outp8) - buffer[2] |= OUT_PORT8; - - DWORD written; - BOOL res = ::WriteFile(m_handle, buffer, USB_BUFSIZE, &written, NULL); - if (!res || written != USB_BUFSIZE) - wxLogError(wxT("Error from WriteFile: err=%u, written=%u"), ::GetLastError(), written); - - m_outp1 = outp1; - m_outp2 = outp2; - m_outp3 = outp3; - m_outp4 = outp4; - m_outp5 = outp5; - m_outp6 = outp6; - m_outp7 = outp7; - m_outp8 = outp8; -} - -void CK8055Controller::close() -{ - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - - setDigitalOutputs(false, false, false, false, false, false, false, false); - - ::CloseHandle(m_handle); - m_handle = INVALID_HANDLE_VALUE; -} - -#else - const unsigned int VELLEMAN_HID_INTERFACE = 0U; const unsigned int USB_OUTPUT_ENDPOINT = 0x01U; @@ -257,27 +60,27 @@ m_outp5(false), m_outp6(false), m_outp7(false), m_outp8(false), -m_context(NULL), -m_handle(NULL) +m_context(nullptr), +m_handle(nullptr) { ::libusb_init(&m_context); } CK8055Controller::~CK8055Controller() { - wxASSERT(m_context != NULL); + assert(m_context != nullptr); ::libusb_exit(m_context); } bool CK8055Controller::open() { - wxASSERT(m_context != NULL); - wxASSERT(m_handle == NULL); + assert(m_context != nullptr); + assert(m_handle == nullptr); m_handle = ::libusb_open_device_with_vid_pid(m_context, VELLEMAN_VENDOR_ID, VELLEMAN_PRODUCT_ID + m_address); - if (m_handle == NULL) { - wxLogError(wxT("Could not open the Velleman K8055")); + if (m_handle == nullptr) { + ::fprintf(stderr, "Could not open the Velleman K8055\n"); return false; } @@ -285,17 +88,17 @@ bool CK8055Controller::open() if (res != 0) { res = ::libusb_detach_kernel_driver(m_handle, VELLEMAN_HID_INTERFACE); if (res != 0) { - wxLogError(wxT("Error from libusb_detach_kernel_driver: err=%d"), res); + ::fprintf(stderr, "Error from libusb_detach_kernel_driver: err=%d\n", res); ::libusb_close(m_handle); - m_handle = NULL; + m_handle = nullptr; return false; } res = ::libusb_claim_interface(m_handle, VELLEMAN_HID_INTERFACE); if (res != 0) { - wxLogError(wxT("Error from libusb_claim_interface: err=%d"), res); + ::fprintf(stderr, "Error from libusb_claim_interface: err=%d\n", res); ::libusb_close(m_handle); - m_handle = NULL; + m_handle = nullptr; return false; } } @@ -309,7 +112,7 @@ bool CK8055Controller::open() void CK8055Controller::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) { - wxASSERT(m_handle != NULL); + assert(m_handle != nullptr); unsigned char buffer[USB_BUFSIZE]; buffer[0] = 0x00; @@ -324,7 +127,7 @@ void CK8055Controller::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool int written; int res = ::libusb_interrupt_transfer(m_handle, USB_INPUT_ENDPOINT, buffer, USB_BUFSIZE, &written, USB_TIMEOUT); if (res != 0) { - wxLogError(wxT("Error from libusb_interrupt_transfer: err=%d"), res); + ::fprintf(stderr, "Error from libusb_interrupt_transfer: err=%d\n", res); return; } @@ -343,7 +146,7 @@ void CK8055Controller::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool void CK8055Controller::setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8) { - wxASSERT(m_handle != NULL); + assert(m_handle != nullptr); if (outp1 == m_outp1 && outp2 == m_outp2 && outp3 == m_outp3 && outp4 == m_outp4 && outp5 == m_outp5 && outp6 == m_outp6 && outp7 == m_outp7 && outp8 == m_outp8) @@ -379,7 +182,7 @@ void CK8055Controller::setDigitalOutputs(bool outp1, bool outp2, bool outp3, boo int written; int res = ::libusb_interrupt_transfer(m_handle, USB_OUTPUT_ENDPOINT, buffer, USB_BUFSIZE, &written, USB_TIMEOUT); if (res != 0) { - wxLogError(wxT("Error from libusb_interrupt_transfer: err=%d"), res); + ::fprintf(stderr, "Error from libusb_interrupt_transfer: err=%d\n", res); return; } @@ -398,15 +201,46 @@ void CK8055Controller::setDigitalOutputs(bool outp1, bool outp2, bool outp3, boo void CK8055Controller::close() { - wxASSERT(m_handle != NULL); + assert(m_handle != nullptr); setDigitalOutputs(false, false, false, false, false, false, false, false); ::libusb_release_interface(m_handle, VELLEMAN_HID_INTERFACE); ::libusb_close(m_handle); - m_handle = NULL; + m_handle = nullptr; } -#endif +#else + +// Windows stub implementation + +CK8055Controller::CK8055Controller(unsigned int address) : +m_address(address) +{ +} + +CK8055Controller::~CK8055Controller() +{ +} +bool CK8055Controller::open() +{ + ::fprintf(stderr, "K8055 not supported on Windows without Velleman DLL\n"); + return false; +} + +void CK8055Controller::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) +{ + inp1 = inp2 = inp3 = inp4 = inp5 = false; +} + +void CK8055Controller::setDigitalOutputs(bool, bool, bool, bool, bool, bool, bool, bool) +{ +} + +void CK8055Controller::close() +{ +} + +#endif diff --git a/Common/K8055Controller.h b/Common/K8055Controller.h index 619e1d9..10177e7 100644 --- a/Common/K8055Controller.h +++ b/Common/K8055Controller.h @@ -16,13 +16,9 @@ #include "HardwareController.h" -#include +#if !defined(_WIN32) -#if defined(__WINDOWS__) -#include -#else #include -#endif class CK8055Controller : public IHardwareController { public: @@ -47,12 +43,32 @@ private: bool m_outp6; bool m_outp7; bool m_outp8; -#if defined(__WINDOWS__) - HANDLE m_handle; -#else libusb_context* m_context; libusb_device_handle* m_handle; -#endif }; +#else + +// Windows stub — K8055 requires the Velleman K8055D.dll which is not bundled. +// Instantiating this class on Windows will log an error and open() will return false. + +class CK8055Controller : public IHardwareController { +public: + CK8055Controller(unsigned int address); + virtual ~CK8055Controller(); + + virtual bool open(); + + virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5); + + virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8); + + virtual void close(); + +private: + unsigned int m_address; +}; + +#endif + #endif diff --git a/Common/LogEvent.cpp b/Common/LogEvent.cpp deleted file mode 100644 index 2cd850c..0000000 --- a/Common/LogEvent.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2010 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "LogEvent.h" - -CLogEvent::CLogEvent(const wxString& text, wxEventType type, int id) : -wxEvent(id, type), -m_text(text) -{ -} - -CLogEvent::CLogEvent(const CLogEvent& event) : -wxEvent(event), -m_text(event.m_text) -{ -} - -CLogEvent::~CLogEvent() -{ -} - -wxString CLogEvent::getText() const -{ - return m_text; -} - -wxEvent* CLogEvent::Clone() const -{ - return new CLogEvent(*this); -} diff --git a/Common/LogEvent.h b/Common/LogEvent.h deleted file mode 100644 index 4721989..0000000 --- a/Common/LogEvent.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2010 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef LogEvent_H -#define LogEvent_H - -#include - -class CLogEvent : public wxEvent { -public: - CLogEvent(const wxString& text, wxEventType type, int id = 0); - virtual ~CLogEvent(); - - virtual wxString getText() const; - - virtual wxEvent* Clone() const; - -protected: - CLogEvent(const CLogEvent& event); - -private: - wxString m_text; -}; - -#endif diff --git a/Common/Logger.cpp b/Common/Logger.cpp index 04de9e3..5203c31 100644 --- a/Common/Logger.cpp +++ b/Common/Logger.cpp @@ -18,121 +18,162 @@ #include "Logger.h" +#include +#include +#include + +static struct tm* gmtime_safe(const time_t* t, struct tm* result) { +#if defined(_WIN32) + gmtime_s(result, t); +#else + gmtime_r(t, result); +#endif + return result; +} + #if defined(MQTT) #include "MQTTConnection.h" -CMQTTConnection* g_mqtt = NULL; +CMQTTConnection* g_mqtt = nullptr; unsigned int g_mqttLevel = 2U; #endif -CLogger::CLogger(const wxString& directory, const wxString& name) : -wxLog(), +std::atomic CLogger::s_instance{nullptr}; + +CLogger::CLogger(const std::string& directory, const std::string& name, + unsigned int fileLevel, unsigned int displayLevel) : m_name(name), -m_file(NULL), -m_fileName(), -m_day(0) +m_directory(directory), +m_file(nullptr), +m_day(0), +m_mutex(), +m_fileLevel(fileLevel), +m_displayLevel(displayLevel) { - m_file = new wxFFile; - - m_fileName.SetPath(directory); - m_fileName.SetExt(wxT("log")); + if (m_fileLevel == 0U) + return; // File logging disabled; do not create a log file. time_t timestamp; ::time(×tamp); - struct tm* tm = ::gmtime(×tamp); - - wxString text; - text.Printf(wxT("%s-%04d-%02d-%02d"), m_name.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + struct tm tmBuf; + struct tm* tm = gmtime_safe(×tamp, &tmBuf); m_day = tm->tm_yday; - m_fileName.SetName(text); + openFile(tm); +} - bool ret = m_file->Open(m_fileName.GetFullPath(), wxT("a+t")); - if (!ret) { - wxLogError(wxT("Cannot open %s file for appending"), m_fileName.GetFullPath().c_str()); - return; +CLogger::~CLogger() +{ + if (m_file != nullptr) { + ::fclose(m_file); + m_file = nullptr; } } -CLogger::~CLogger() +CLogger* CLogger::getInstance() { - wxASSERT(m_file != NULL); + return s_instance; +} - m_file->Close(); - delete m_file; +void CLogger::setInstance(CLogger* logger) +{ + s_instance = logger; } -void CLogger::DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info) +void CLogger::openFile(const struct tm* tm) { - wxASSERT(m_file != NULL); - wxASSERT(m_file->IsOpened()); + if (m_file != nullptr) + ::fclose(m_file); - wxString letter; + char filename[512]; + ::snprintf(filename, sizeof(filename), "%s/%s-%04d-%02d-%02d.log", + m_directory.c_str(), m_name.c_str(), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); - switch (level) { - case wxLOG_FatalError: letter = wxT("F"); break; - case wxLOG_Error: letter = wxT("E"); break; - case wxLOG_Warning: letter = wxT("W"); break; - case wxLOG_Info: letter = wxT("I"); break; - case wxLOG_Message: letter = wxT("M"); break; - case wxLOG_Status: letter = wxT("M"); break; - case wxLOG_Trace: letter = wxT("T"); break; - case wxLOG_Debug: letter = wxT("D"); break; - default: letter = wxT("U"); break; - } + m_file = ::fopen(filename, "a+t"); + if (m_file == nullptr) + ::fprintf(stderr, "Cannot open %s for appending\n", filename); +} + +void CLogger::log(LogLevel level, const char* fmt, ...) +{ + // Fast-path: drop the message if neither sink would accept it. + const unsigned int numLevel = static_cast(level); + const bool toFile = (m_fileLevel != 0U && numLevel >= m_fileLevel); + const bool toDisplay = (m_displayLevel != 0U && numLevel >= m_displayLevel); +#if defined(MQTT) + const bool toMQTT = (g_mqtt != nullptr && g_mqttLevel != 0U && numLevel >= g_mqttLevel); +#else + const bool toMQTT = false; +#endif + if (!toFile && !toDisplay && !toMQTT && level != LOG_FATAL) + return; - struct tm* tm = ::gmtime(&info.timestamp); + char buffer[1024]; - wxString message; - message.Printf(wxT("%s: %04d-%02d-%02d %02d:%02d:%02d: %s\n"), letter.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, msg.c_str()); + va_list ap; + va_start(ap, fmt); + ::vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); - writeLog(message.c_str(), info.timestamp); + time_t timestamp; + ::time(×tamp); -#if defined(MQTT) - if (g_mqtt != NULL && g_mqttLevel != 0U) { - unsigned int numLevel = 0U; - switch (level) { - case wxLOG_FatalError: numLevel = 6U; break; - case wxLOG_Error: numLevel = 5U; break; - case wxLOG_Warning: numLevel = 4U; break; - case wxLOG_Info: numLevel = 3U; break; - case wxLOG_Message: numLevel = 2U; break; - default: numLevel = 1U; break; + const char* letter; + switch (level) { + case LOG_FATAL: letter = "F"; break; + case LOG_ERROR: letter = "E"; break; + case LOG_WARNING: letter = "W"; break; + case LOG_INFO: letter = "I"; break; + case LOG_MESSAGE: letter = "M"; break; + case LOG_DEBUG: letter = "D"; break; + default: letter = "U"; break; + } + + struct tm tmBuf; + struct tm* tm = gmtime_safe(×tamp, &tmBuf); + + char message[1280]; + ::snprintf(message, sizeof(message), "%s: %04d-%02d-%02d %02d:%02d:%02d: %s\n", + letter, + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + buffer); + + if (toFile || toDisplay) { + std::lock_guard lock(m_mutex); + if (toFile) + writeLog(message, timestamp); + if (toDisplay) { + ::fputs(message, stdout); + ::fflush(stdout); } - if (numLevel >= g_mqttLevel) - g_mqtt->publish("log", (const char*)message.mb_str()); } + + // MQTT publish happens outside the lock: CMQTTConnection has its own + // internal locking and mosquitto_publish() must not be called while + // holding m_mutex (risk of priority inversion / deadlock). +#if defined(MQTT) + if (toMQTT) + g_mqtt->publish("log", message); #endif - if (level == wxLOG_FatalError) + if (level == LOG_FATAL) ::abort(); } -void CLogger::writeLog(const wxChar* msg, time_t timestamp) +void CLogger::writeLog(const char* msg, time_t timestamp) { - wxASSERT(m_file != NULL); - wxASSERT(m_file->IsOpened()); - wxASSERT(msg != NULL); - - struct tm* tm = ::gmtime(×tamp); + struct tm tmBuf; + struct tm* tm = gmtime_safe(×tamp, &tmBuf); int day = tm->tm_yday; if (day != m_day) { - wxString text; - text.Printf(wxT("%s-%04d-%02d-%02d"), m_name.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); - m_day = day; - m_fileName.SetName(text); - - m_file->Close(); - - bool ret = m_file->Open(m_fileName.GetFullPath(), wxT("a+t")); - if (!ret) { - wxLogError(wxT("Cannot open %s file for appending"), m_fileName.GetFullPath().c_str()); - return; - } + openFile(tm); } - m_file->Write(wxString(msg)); - m_file->Flush(); + if (m_file != nullptr) { + ::fputs(msg, m_file); + ::fflush(m_file); + } } - diff --git a/Common/Logger.h b/Common/Logger.h index 72e7368..77242ea 100644 --- a/Common/Logger.h +++ b/Common/Logger.h @@ -16,34 +16,96 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* + * Singleton, thread-safe file logger with daily log rotation. + * + * One CLogger instance is created at startup and registered via setInstance(). + * All threads call the static getInstance() accessor or, more commonly, use the + * wxLogXxx macros below. The macros are compatibility shims that preserve the + * original wxWidgets wxLogMessage/wxLogError call sites without pulling in the + * wxWidgets library. + * + * Log levels follow the MMDVMHost convention so that config file values map + * directly to enum values without translation: + * + * Config value 0 — no logging (disabled) + * Config value 1 — LOG_DEBUG (most verbose) + * Config value 2 — LOG_MESSAGE + * Config value 3 — LOG_INFO + * Config value 4 — LOG_WARNING + * Config value 5 — LOG_ERROR + * Config value 6 — LOG_FATAL (least verbose) + * + * Both fileLevel and displayLevel use these values: a message is written to + * the respective sink only when its level >= the configured threshold. Setting + * a threshold to 0 disables that sink entirely; 6 passes only LOG_FATAL. + * + * When built with MQTT=1, log lines are also forwarded to the MQTT broker on + * the "{name}/log" topic via the global g_mqtt connection, filtered by the + * minimum level stored in g_mqttLevel. + */ + #ifndef Logger_H #define Logger_H -#include -#include -#include +#include +#include +#include +#include #if defined(MQTT) class CMQTTConnection; -extern CMQTTConnection* g_mqtt; -extern unsigned int g_mqttLevel; +extern CMQTTConnection* g_mqtt; // Global MQTT connection (set in main()) +extern unsigned int g_mqttLevel; // Minimum LogLevel to forward over MQTT #endif -class CLogger : public wxLog { +// Ordered to match MMDVMHost: enum value == config file level. +// Level 0 ("no logging") has no corresponding enum constant. +enum LogLevel { + LOG_DEBUG = 1, + LOG_MESSAGE = 2, + LOG_INFO = 3, + LOG_WARNING = 4, + LOG_ERROR = 5, + LOG_FATAL = 6 +}; + +class CLogger { public: - CLogger(const wxString& directory, const wxString& name); - virtual ~CLogger(); + // directory — where daily log files are written; may be empty when fileLevel == 0. + // name — base filename stem (e.g. "dstarrepeaterd"). + // fileLevel — minimum LogLevel to write to the log file (0 = disabled). + // displayLevel — minimum LogLevel to write to stdout (0 = disabled). + CLogger(const std::string& directory, const std::string& name, + unsigned int fileLevel = 2U, unsigned int displayLevel = 2U); + ~CLogger(); + + void log(LogLevel level, const char* fmt, ...) __attribute__((format(printf, 3, 4))); - virtual void DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info); + static CLogger* getInstance(); + static void setInstance(CLogger* logger); private: - wxString m_name; - wxFFile* m_file; - wxFileName m_fileName; - int m_day; + std::string m_name; // Base filename stem (e.g. "dstarrepeater") + std::string m_directory; // Directory where daily log files are written + FILE* m_file; // Currently open log file handle (nullptr when disabled) + int m_day; // Day-of-year when m_file was opened; triggers rotation + std::mutex m_mutex; // Serialises concurrent log() calls from multiple threads + unsigned int m_fileLevel; // Minimum level to write to file (0 = disabled) + unsigned int m_displayLevel; // Minimum level to write to stdout (0 = disabled) - void writeLog(const wxChar* msg, time_t timestamp); + static std::atomic s_instance; // The singleton logger + + void openFile(const struct tm* tm); + void writeLog(const char* msg, time_t timestamp); }; -#endif +// Compatibility macros matching the wxWidgets wxLogXxx API. Existing call +// sites throughout the codebase use these names and can remain unchanged. +#define wxLogFatalError(fmt, ...) do { if (CLogger::getInstance()) CLogger::getInstance()->log(LOG_FATAL, fmt, ##__VA_ARGS__); } while(0) +#define wxLogError(fmt, ...) do { if (CLogger::getInstance()) CLogger::getInstance()->log(LOG_ERROR, fmt, ##__VA_ARGS__); } while(0) +#define wxLogWarning(fmt, ...) do { if (CLogger::getInstance()) CLogger::getInstance()->log(LOG_WARNING, fmt, ##__VA_ARGS__); } while(0) +#define wxLogMessage(fmt, ...) do { if (CLogger::getInstance()) CLogger::getInstance()->log(LOG_MESSAGE, fmt, ##__VA_ARGS__); } while(0) +#define wxLogInfo(fmt, ...) do { if (CLogger::getInstance()) CLogger::getInstance()->log(LOG_INFO, fmt, ##__VA_ARGS__); } while(0) +#endif diff --git a/Common/MMDVMController.cpp b/Common/MMDVMController.cpp index c101c96..08a3823 100644 --- a/Common/MMDVMController.cpp +++ b/Common/MMDVMController.cpp @@ -19,12 +19,16 @@ #include "CCITTChecksumReverse.h" #include "MMDVMController.h" #include "DStarDefines.h" +#include "Logger.h" #include "Timer.h" -#if defined(__WINDOWS__) -#include -#else -#include +#include +#include +#include +#include +#if !defined(_WIN32) +#include +#include #endif const unsigned char MMDVM_FRAME_START = 0xE0U; @@ -52,7 +56,7 @@ const unsigned int MAX_RESPONSES = 30U; const unsigned int BUFFER_LENGTH = 200U; -CMMDVMController::CMMDVMController(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel) : +CMMDVMController::CMMDVMController(const std::string& port, const std::string& path, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel) : CModem(), m_port(port), m_path(path), @@ -63,11 +67,11 @@ m_txDelay(txDelay), m_rxLevel(rxLevel), m_txLevel(txLevel), m_serial(port, SERIAL_115200, true), -m_buffer(NULL), +m_buffer(nullptr), m_txData(1000U), m_rx(false) { - wxASSERT(!port.IsEmpty()); + assert(!port.empty()); m_buffer = new unsigned char[BUFFER_LENGTH]; } @@ -87,16 +91,14 @@ bool CMMDVMController::start() findPath(); - Create(); - SetPriority(100U); - Run(); + m_thread = std::thread(&CMMDVMController::entry, this); return true; } -void* CMMDVMController::Entry() +void CMMDVMController::entry() { - wxLogMessage(wxT("Starting MMDVM Controller thread")); + wxLogMessage("Starting MMDVM Controller thread"); // Clock every 5ms-ish CTimer pollTimer(200U, 0U, 100U); @@ -115,8 +117,9 @@ void* CMMDVMController::Entry() if (!ret) { ret = findModem(); if (!ret) { - wxLogError(wxT("Stopping MMDVM Controller thread")); - return NULL; + wxLogError("Stopping MMDVM Controller thread"); + delete[] writeBuffer; + return; } } @@ -133,15 +136,16 @@ void* CMMDVMController::Entry() case RTDVM_ERROR: { bool ret = findModem(); if (!ret) { - wxLogError(wxT("Stopping MMDVM Controller thread")); - return NULL; + wxLogError("Stopping MMDVM Controller thread"); + delete[] writeBuffer; + return; } } break; case RTDVM_DSTAR_HEADER: { - // CUtils::dump(wxT("RT_DSTAR_HEADER"), m_buffer, length); - wxMutexLocker locker(m_mutex); + // CUtils::dump("RT_DSTAR_HEADER", m_buffer, length); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_HEADER; @@ -155,8 +159,8 @@ void* CMMDVMController::Entry() break; case RTDVM_DSTAR_DATA: { - // CUtils::dump(wxT("RT_DSTAR_DATA"), m_buffer, length); - wxMutexLocker locker(m_mutex); + // CUtils::dump("RT_DSTAR_DATA", m_buffer, length); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_DATA; @@ -170,8 +174,8 @@ void* CMMDVMController::Entry() break; case RTDVM_DSTAR_EOT: { - // wxLogMessage(wxT("RT_DSTAR_EOT")); - wxMutexLocker locker(m_mutex); + // wxLogMessage("RT_DSTAR_EOT"); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; @@ -183,8 +187,8 @@ void* CMMDVMController::Entry() break; case RTDVM_DSTAR_LOST: { - // wxLogMessage(wxT("RT_DSTAR_LOST")); - wxMutexLocker locker(m_mutex); + // wxLogMessage("RT_DSTAR_LOST"); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_LOST; @@ -198,20 +202,21 @@ void* CMMDVMController::Entry() case RTDVM_GET_STATUS: { bool dstar = (m_buffer[3U] & 0x01U) == 0x01U; if (!dstar) { - wxLogError(wxT("D-Star not enabled in the MMDVM!!!")); - wxLogError(wxT("Stopping MMDVM Controller thread")); - return NULL; + wxLogError("D-Star not enabled in the MMDVM!!!"); + wxLogError("Stopping MMDVM Controller thread"); + delete[] writeBuffer; + return; } m_tx = (m_buffer[5U] & 0x01U) == 0x01U; bool adcOverflow = (m_buffer[5U] & 0x02U) == 0x02U; if (adcOverflow) - wxLogWarning(wxT("MMDVM ADC levels have overflowed")); + wxLogWarning("MMDVM ADC levels have overflowed"); space = m_buffer[6U]; - // CUtils::dump(wxT("GET_STATUS"), m_buffer, length); - // wxLogMessage(wxT("PTT=%d space=%u"), int(m_tx), space); + // CUtils::dump("GET_STATUS", m_buffer, length); + // wxLogMessage("PTT=%d space=%u", int(m_tx), space); } break; @@ -223,23 +228,23 @@ void* CMMDVMController::Entry() case RTDVM_NAK: { switch (m_buffer[3U]) { case MMDVM_DSTAR_HEADER: - wxLogWarning(wxT("Received a header NAK from the MMDVM, reason = %u"), m_buffer[4U]); + wxLogWarning("Received a header NAK from the MMDVM, reason = %u", m_buffer[4U]); break; case MMDVM_DSTAR_DATA: - wxLogWarning(wxT("Received a data NAK from the MMDVM, reason = %u"), m_buffer[4U]); + wxLogWarning("Received a data NAK from the MMDVM, reason = %u", m_buffer[4U]); break; case MMDVM_DSTAR_EOT: - wxLogWarning(wxT("Received an EOT NAK from the MMDVM, reason = %u"), m_buffer[4U]); + wxLogWarning("Received an EOT NAK from the MMDVM, reason = %u", m_buffer[4U]); break; default: - wxLogWarning(wxT("Received a NAK from the MMDVM, command = 0x%02X, reason = %u"), m_buffer[3U], m_buffer[4U]); + wxLogWarning("Received a NAK from the MMDVM, command = 0x%02X, reason = %u", m_buffer[3U], m_buffer[4U]); break; } } break; case RTDVM_DUMP: - CUtils::dump(wxT("Modem dump"), m_buffer + 3U, length - 3U); + CUtils::dump("Modem dump", m_buffer + 3U, length - 3U); break; case RTDVM_DEBUG1: @@ -251,13 +256,13 @@ void* CMMDVMController::Entry() break; default: - wxLogMessage(wxT("Unknown message, type: %02X"), m_buffer[2U]); - CUtils::dump(wxT("Buffer dump"), m_buffer, length); + wxLogMessage("Unknown message, type: %02X", m_buffer[2U]); + CUtils::dump("Buffer dump", m_buffer, length); break; } if (writeType == DSMTT_NONE && m_txData.hasData()) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); m_txData.getData(&writeType, 1U); m_txData.getData(&writeLength, 1U); @@ -265,46 +270,44 @@ void* CMMDVMController::Entry() } if (space > 4U && writeType == DSMTT_HEADER) { - // CUtils::dump(wxT("Write Header"), writeBuffer, writeLength); + // CUtils::dump("Write Header", writeBuffer, writeLength); int ret = m_serial.write(writeBuffer, writeLength); if (ret != int(writeLength)) - wxLogWarning(wxT("Error when writing the header to the MMDVM")); + wxLogWarning("Error when writing the header to the MMDVM"); writeType = DSMTT_NONE; space -= 4U; } if (space > 1U && (writeType == DSMTT_DATA || writeType == DSMTT_EOT)) { - // CUtils::dump(wxT("Write Data"), writeBuffer, writeLength); + // CUtils::dump("Write Data", writeBuffer, writeLength); int ret = m_serial.write(writeBuffer, writeLength); if (ret != int(writeLength)) - wxLogWarning(wxT("Error when writing data to the MMDVM")); + wxLogWarning("Error when writing data to the MMDVM"); writeType = DSMTT_NONE; space--; } - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); pollTimer.clock(); } - wxLogMessage(wxT("Stopping MMDVM Controller thread")); + wxLogMessage("Stopping MMDVM Controller thread"); delete[] writeBuffer; m_serial.close(); - - return NULL; } bool CMMDVMController::writeHeader(const CHeaderData& header) { bool ret = m_txData.hasSpace(46U); if (!ret) { - wxLogWarning(wxT("No space to write the header")); + wxLogWarning("No space to write the header"); return false; } @@ -320,31 +323,31 @@ bool CMMDVMController::writeHeader(const CHeaderData& header) buffer[4U] = header.getFlag2(); buffer[5U] = header.getFlag3(); - wxString rpt2 = header.getRptCall2(); - for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 6U] = rpt2.GetChar(i); + std::string rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 6U] = rpt2[i]; - wxString rpt1 = header.getRptCall1(); - for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 14U] = rpt1.GetChar(i); + std::string rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 14U] = rpt1[i]; - wxString your = header.getYourCall(); - for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 22U] = your.GetChar(i); + std::string your = header.getYourCall(); + for (unsigned int i = 0U; i < your.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 22U] = your[i]; - wxString my1 = header.getMyCall1(); - for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 30U] = my1.GetChar(i); + std::string my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 30U] = my1[i]; - wxString my2 = header.getMyCall2(); - for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) - buffer[i + 38U] = my2.GetChar(i); + std::string my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.size() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 38U] = my2[i]; CCCITTChecksumReverse cksum1; cksum1.update(buffer + 3U, RADIO_HEADER_LENGTH_BYTES - 2U); cksum1.result(buffer + 42U); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char type = DSMTT_HEADER; m_txData.addData(&type, 1U); @@ -361,7 +364,7 @@ bool CMMDVMController::writeData(const unsigned char* data, unsigned int length, { bool ret = m_txData.hasSpace(17U); if (!ret) { - wxLogWarning(wxT("No space to write data")); + wxLogWarning("No space to write data"); return false; } @@ -372,7 +375,7 @@ bool CMMDVMController::writeData(const unsigned char* data, unsigned int length, buffer[1U] = 3U; buffer[2U] = MMDVM_DSTAR_EOT; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char type = DSMTT_EOT; m_txData.addData(&type, 1U); @@ -390,7 +393,7 @@ bool CMMDVMController::writeData(const unsigned char* data, unsigned int length, buffer[2U] = MMDVM_DSTAR_DATA; ::memcpy(buffer + 3U, data, DV_FRAME_LENGTH_BYTES); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char type = DSMTT_DATA; m_txData.addData(&type, 1U); @@ -416,9 +419,13 @@ bool CMMDVMController::isTXReady() return m_txData.isEmpty(); } +// Waits 2s for the MMDVM firmware to finish booting, then retries GET_VERSION +// up to 6 times (with 1s between attempts) before giving up. The 2s initial +// wait is required because the Arduino bootloader holds the serial port for +// approximately that period after a USB connection is established. bool CMMDVMController::readVersion() { - ::wxSleep(2); + std::this_thread::sleep_for(std::chrono::seconds(2)); for (unsigned int i = 0U; i < 6U; i++) { unsigned char buffer[3U]; @@ -427,30 +434,30 @@ bool CMMDVMController::readVersion() buffer[1U] = 3U; buffer[2U] = MMDVM_GET_VERSION; - // CUtils::dump(wxT("Written"), buffer, 3U); + // CUtils::dump("Written", buffer, 3U); int ret = m_serial.write(buffer, 3U); if (ret != 3) return false; for (unsigned int count = 0U; count < MAX_RESPONSES; count++) { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); unsigned int length; RESP_TYPE_MMDVM resp = getResponse(m_buffer, length); if (resp == RTDVM_GET_VERSION) { - wxString description((char*)(m_buffer + 4U), wxConvLocal, length - 4U); + std::string description((char*)(m_buffer + 4U), length - 4U); - wxLogInfo(wxT("MMDVM protocol version: %u, description: %s"), m_buffer[3U], description.c_str()); + wxLogInfo("MMDVM protocol version: %u, description: %s", m_buffer[3U], description.c_str()); return true; } } - ::wxSleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); } - wxLogError(wxT("Unable to read the firmware version after six attempts")); + wxLogError("Unable to read the firmware version after six attempts"); return false; } @@ -466,6 +473,14 @@ bool CMMDVMController::readStatus() return m_serial.write(buffer, 3U) == 3; } +// Sends SET_CONFIG (20-byte frame) to configure the MMDVM for D-Star only. +// Key fields: +// byte[3] invert flags: bit0=rxInvert, bit1=txInvert, bit2=pttInvert +// byte[4] mode mask: 0x01 = D-Star enabled, all other modes off +// byte[5] txDelay: value / 10ms units (e.g. txDelay=100 -> 10) +// byte[6] initial state: 1 = STATE_DSTAR +// byte[7] rxLevel, byte[8] txLevel: 0-100% scaled to 0-255 +// bytes[12-15,18] txLevel repeated for DMR/YSF/P25/NXDN (unused but required) bool CMMDVMController::setConfig() { unsigned char buffer[25U]; @@ -503,7 +518,7 @@ bool CMMDVMController::setConfig() buffer[13U] = (m_txLevel * 255U) / 100U; buffer[14U] = (m_txLevel * 255U) / 100U; buffer[15U] = (m_txLevel * 255U) / 100U; - + buffer[16U] = 128U; buffer[17U] = 128U; @@ -511,7 +526,7 @@ bool CMMDVMController::setConfig() buffer[19U] = 0U; - // CUtils::dump(wxT("Written"), buffer, 20U); + // CUtils::dump("Written", buffer, 20U); int ret = m_serial.write(buffer, 20U); if (ret != 20U) @@ -521,23 +536,23 @@ bool CMMDVMController::setConfig() unsigned int length; RESP_TYPE_MMDVM resp; do { - ::wxMilliSleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); resp = getResponse(m_buffer, length); if (resp != RTDVM_ACK && resp != RTDVM_NAK) { count++; if (count >= MAX_RESPONSES) { - wxLogError(wxT("The MMDVM is not responding to the SET_CONFIG command")); + wxLogError("The MMDVM is not responding to the SET_CONFIG command"); return false; } } } while (resp != RTDVM_ACK && resp != RTDVM_NAK); - // CUtils::dump(wxT("Response"), m_buffer, length); + // CUtils::dump("Response", m_buffer, length); if (resp == RTDVM_NAK) { - wxLogError(wxT("Received a NAK to the SET_CONFIG command from the modem")); + wxLogError("Received a NAK to the SET_CONFIG command from the modem"); return false; } @@ -549,7 +564,7 @@ RESP_TYPE_MMDVM CMMDVMController::getResponse(unsigned char *buffer, unsigned in // Get the start of the frame or nothing at all int ret = m_serial.read(buffer + 0U, 1U); if (ret < 0) { - wxLogError(wxT("Error when reading from the MMDVM")); + wxLogError("Error when reading from the MMDVM"); return RTDVM_ERROR; } @@ -561,7 +576,7 @@ RESP_TYPE_MMDVM CMMDVMController::getResponse(unsigned char *buffer, unsigned in ret = m_serial.read(buffer + 1U, 1U); if (ret < 0) { - wxLogError(wxT("Error when reading from the MMDVM")); + wxLogError("Error when reading from the MMDVM"); return RTDVM_ERROR; } @@ -571,8 +586,8 @@ RESP_TYPE_MMDVM CMMDVMController::getResponse(unsigned char *buffer, unsigned in length = buffer[1U]; if (length >= BUFFER_LENGTH) { - wxLogError(wxT("Invalid data received from the MMDVM")); - CUtils::dump(wxT("Data"), buffer, 2U); + wxLogError("Invalid data received from the MMDVM"); + CUtils::dump("Data", buffer, 2U); return RTDVM_TIMEOUT; } @@ -581,18 +596,21 @@ RESP_TYPE_MMDVM CMMDVMController::getResponse(unsigned char *buffer, unsigned in while (offset < length) { int ret = m_serial.read(buffer + offset, length - offset); if (ret < 0) { - wxLogError(wxT("Error when reading from the MMDVM")); + wxLogError("Error when reading from the MMDVM"); return RTDVM_ERROR; } if (ret > 0) offset += ret; - if (ret == 0) - Sleep(5UL); + if (ret == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + if (m_stopped) + return RTDVM_TIMEOUT; + } } - // CUtils::dump(wxT("Received"), buffer, length); + // CUtils::dump("Received", buffer, length); switch (buffer[2U]) { case MMDVM_GET_STATUS: @@ -628,151 +646,109 @@ RESP_TYPE_MMDVM CMMDVMController::getResponse(unsigned char *buffer, unsigned in } } -wxString CMMDVMController::getPath() const +std::string CMMDVMController::getPath() const { return m_path; } bool CMMDVMController::findPort() { - if (m_path.IsEmpty()) +#if !defined(_WIN32) + if (m_path.empty()) return false; -#if defined(__WINDOWS__) -#else - wxDir dir; - bool ret1 = dir.Open(wxT("/sys/class/tty")); - if (!ret1) { - wxLogError(wxT("Cannot open directory /sys/class/tty")); + DIR* dir = ::opendir("/sys/class/tty"); + if (dir == nullptr) { + wxLogError("Cannot open directory /sys/class/tty"); return false; } - wxString fileName; - ret1 = dir.GetFirst(&fileName, wxT("ttyACM*")); - while (ret1) { - wxString path; - path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str()); + struct dirent* entry; + while ((entry = ::readdir(dir)) != nullptr) { + std::string fileName(entry->d_name); - char cpath[255U]; - ::memset(cpath, 0x00U, 255U); + // Match ttyACM* entries + if (fileName.substr(0, 6) != "ttyACM") + continue; + + std::string path = "/sys/class/tty/" + fileName; - for (unsigned int i = 0U; i < path.Len(); i++) - cpath[i] = path.GetChar(i); + char cpath[255U]; + ::strncpy(cpath, path.c_str(), sizeof(cpath) - 1); + cpath[sizeof(cpath) - 1] = '\0'; char symlink[255U]; - int ret2 = ::readlink(cpath, symlink, 255U); + int ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret2 < 0) { - ::strcat(cpath, "/device"); - ret2 = ::readlink(cpath, symlink, 255U); + ::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1); + ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret2 < 0) { - wxLogError(wxT("Error from readlink()")); + wxLogError("Error from readlink()"); + ::closedir(dir); return false; } - - path = wxString(symlink, wxConvLocal, ret2); + symlink[ret2] = '\0'; + path = std::string(symlink, ret2); } else { - // Get all but the last section - wxString fullPath = wxString(symlink, wxConvLocal, ret2); - path = fullPath.BeforeLast(wxT('/')); + symlink[ret2] = '\0'; + std::string fullPath(symlink, ret2); + size_t pos = fullPath.rfind('/'); + path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath; } - if (path.IsSameAs(m_path)) { - m_port.Printf(wxT("/dev/%s"), fileName.c_str()); + if (path == m_path) { + m_port = "/dev/" + fileName; - wxLogMessage(wxT("Found modem port of %s based on the path"), m_port.c_str()); + wxLogMessage("Found modem port of %s based on the path", m_port.c_str()); + ::closedir(dir); return true; } - - ret1 = dir.GetNext(&fileName); } -#endif + ::closedir(dir); return false; +#else + return true; +#endif } bool CMMDVMController::findPath() { -#if defined(__WINDOWS__) -#ifdef notdef - GUID guids[5U]; - - DWORD count; - BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count); - if (!res) { - wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError()); - return false; - } - - for (DWORD i = 0U; i < count; i++) { - HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); - if (devInfo == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); - return false; - } - - SP_DEVICE_INTERFACE_DATA devInfoData; - devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - - for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) { - // Find the required length of the device structure - DWORD length; - ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); - - PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); - detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - // Get the detailed data into the newly allocated device structure - DWORD required; - res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); - if (!res) { - wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - ::free(detailData); - } - - ::SetupDiDestroyDeviceInfoList(devInfo); - } - - return false; -#endif -#else - wxString path; - path.Printf(wxT("/sys/class/tty/%s"), m_port.Mid(5U).c_str()); +#if !defined(_WIN32) + std::string path = "/sys/class/tty/" + m_port.substr(5U); char cpath[255U]; - ::memset(cpath, 0x00U, 255U); - - for (unsigned int i = 0U; i < path.Len(); i++) - cpath[i] = path.GetChar(i); + ::strncpy(cpath, path.c_str(), sizeof(cpath) - 1); + cpath[sizeof(cpath) - 1] = '\0'; char symlink[255U]; - int ret = ::readlink(cpath, symlink, 255U); + int ret = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret < 0) { - ::strcat(cpath, "/device"); - ret = ::readlink(cpath, symlink, 255U); + ::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1); + ret = ::readlink(cpath, symlink, sizeof(symlink) - 1); if (ret < 0) { - wxLogError(wxT("Error from readlink()")); + wxLogError("Error from readlink()"); return false; } - - path = wxString(symlink, wxConvLocal, ret); + symlink[ret] = '\0'; + path = std::string(symlink, ret); } else { - wxString fullPath = wxString(symlink, wxConvLocal, ret); - path = fullPath.BeforeLast(wxT('/')); + symlink[ret] = '\0'; + std::string fullPath(symlink, ret); + size_t pos = fullPath.rfind('/'); + path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath; } - if (m_path.IsEmpty()) - wxLogMessage(wxT("Found modem path of %s"), path.c_str()); + if (m_path.empty()) + wxLogMessage("Found modem path of %s", path.c_str()); m_path = path; -#endif return true; +#else + return true; +#endif } bool CMMDVMController::findModem() @@ -781,7 +757,7 @@ bool CMMDVMController::findModem() // Tell the repeater that the signal has gone away if (m_rx) { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; @@ -797,7 +773,7 @@ bool CMMDVMController::findModem() while (!m_stopped) { count++; if (count >= 4U) { - wxLogMessage(wxT("Trying to reopen the modem")); + wxLogMessage("Trying to reopen the modem"); bool ret = findPort(); if (ret) { @@ -809,7 +785,7 @@ bool CMMDVMController::findModem() count = 0U; } - Sleep(500UL); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); } return false; @@ -839,30 +815,30 @@ bool CMMDVMController::openModem() void CMMDVMController::printDebug() { unsigned int length = m_buffer[1U]; - if (m_buffer[2U] == 0xF1U) { - wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 3U); - wxLogMessage(wxT("Debug: %s"), message.c_str()); - } else if (m_buffer[2U] == 0xF2U) { - wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 5U); + if (m_buffer[2U] == 0xF1U && length >= 4U) { + std::string message((char*)(m_buffer + 3U), length - 3U); + wxLogMessage("Debug: %s", message.c_str()); + } else if (m_buffer[2U] == 0xF2U && length >= 5U) { + std::string message((char*)(m_buffer + 3U), length - 5U); short val1 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; - wxLogMessage(wxT("Debug: %s %d"), message.c_str(), val1); - } else if (m_buffer[2U] == 0xF3U) { - wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 7U); + wxLogMessage("Debug: %s %d", message.c_str(), val1); + } else if (m_buffer[2U] == 0xF3U && length >= 7U) { + std::string message((char*)(m_buffer + 3U), length - 7U); short val1 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U]; short val2 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; - wxLogMessage(wxT("Debug: %s %d %d"), message.c_str(), val1, val2); - } else if (m_buffer[2U] == 0xF4U) { - wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 9U); + wxLogMessage("Debug: %s %d %d", message.c_str(), val1, val2); + } else if (m_buffer[2U] == 0xF4U && length >= 9U) { + std::string message((char*)(m_buffer + 3U), length - 9U); short val1 = (m_buffer[length - 6U] << 8) | m_buffer[length - 5U]; short val2 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U]; short val3 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; - wxLogMessage(wxT("Debug: %s %d %d %d"), message.c_str(), val1, val2, val3); - } else if (m_buffer[2U] == 0xF5U) { - wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 11U); + wxLogMessage("Debug: %s %d %d %d", message.c_str(), val1, val2, val3); + } else if (m_buffer[2U] == 0xF5U && length >= 11U) { + std::string message((char*)(m_buffer + 3U), length - 11U); short val1 = (m_buffer[length - 8U] << 8) | m_buffer[length - 7U]; short val2 = (m_buffer[length - 6U] << 8) | m_buffer[length - 5U]; short val3 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U]; short val4 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; - wxLogMessage(wxT("Debug: %s %d %d %d %d"), message.c_str(), val1, val2, val3, val4); + wxLogMessage("Debug: %s %d %d %d %d", message.c_str(), val1, val2, val3, val4); } } diff --git a/Common/MMDVMController.h b/Common/MMDVMController.h index 0151c50..085465c 100644 --- a/Common/MMDVMController.h +++ b/Common/MMDVMController.h @@ -23,8 +23,9 @@ #include "RingBuffer.h" #include "Modem.h" #include "Utils.h" +#include "StdCompat.h" -#include +#include enum RESP_TYPE_MMDVM { RTDVM_TIMEOUT, @@ -46,13 +47,52 @@ enum RESP_TYPE_MMDVM { RTDVM_DEBUG5 }; +/* + * CMMDVMController - Driver for MMDVM (Multi-Mode Digital Voice Modem) boards. + * + * Hardware interface: USB CDC-ACM serial at 115200 baud with RTS/CTS flow control. + * + * Protocol: Binary, 3-byte minimum frame. + * byte[0] = 0xE0 (MMDVM_FRAME_START, always) + * byte[1] = total frame length (including this 3-byte header) + * byte[2] = frame type (command/response code) + * byte[3..] = payload + * + * Frame types used for D-Star: + * 0x00 GET_VERSION - request firmware version string + * 0x01 GET_STATUS - request modem status (TX state, space, ADC overflow) + * 0x02 SET_CONFIG - configure modem (invert flags, levels, txDelay, mode) + * 0x10 DSTAR_HEADER - D-Star header (TX from host, RX from modem) + * 0x11 DSTAR_DATA - D-Star voice frame + * 0x12 DSTAR_LOST - RX sync lost (modem -> host) + * 0x13 DSTAR_EOT - end of transmission + * 0x70 ACK - positive acknowledgement + * 0x7F NAK - negative acknowledgement (with reason byte) + * 0xF0 DUMP - raw hex dump from firmware + * 0xF1-0xF5 DEBUG1-5 - firmware debug messages with 0-4 int16 parameters + * + * Startup sequence (readVersion waits 2s for the firmware to boot): + * readVersion() -> setConfig() -> launch entry() thread + * + * During operation (entry() thread): + * - Sends GET_STATUS every 100ms (readStatus()), which triggers a status + * response containing the current TX flag and remaining TX buffer space. + * - Buffer space is used to gate outbound header and data writes: + * space > 4 required to send a header (4 frames reserved) + * space > 1 required to send a data frame + * + * pttInvert: inverts the PTT output polarity (useful when the radio uses + * active-low PTT keying). + * rxLevel / txLevel: 0-100%, scaled to 0-255 for the modem's ADC/DAC ranges. + * + * Auto-detection (findPort / findPath): same sysfs USB path mechanism as + * CDVMegaController; allows the modem to be found after a USB re-enumeration. + */ class CMMDVMController : public CModem { public: - CMMDVMController(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel); + CMMDVMController(const std::string& port, const std::string& path, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel); virtual ~CMMDVMController(); - virtual void* Entry(); - virtual bool start(); virtual unsigned int getSpace(); @@ -61,11 +101,13 @@ public: virtual bool writeHeader(const CHeaderData& header); virtual bool writeData(const unsigned char* data, unsigned int length, bool end); - virtual wxString getPath() const; + virtual std::string getPath() const; private: - wxString m_port; - wxString m_path; + void entry(); + + std::string m_port; + std::string m_path; bool m_rxInvert; bool m_txInvert; bool m_pttInvert; @@ -89,8 +131,9 @@ private: bool findModem(); bool openModem(); + // Decodes and logs a DEBUG1-DEBUG5 firmware debug frame. + // DEBUG1 carries a string; DEBUG2-5 append 1-4 big-endian int16 values. void printDebug(); }; #endif - diff --git a/Common/MQTTConnection.cpp b/Common/MQTTConnection.cpp index 7c63241..d49cd83 100644 --- a/Common/MQTTConnection.cpp +++ b/Common/MQTTConnection.cpp @@ -23,9 +23,9 @@ #include #include #include - -#if defined(_WIN32) || defined(_WIN64) +#if defined(_WIN32) #include +#define getpid _getpid #else #include #endif @@ -60,11 +60,7 @@ CMQTTConnection::~CMQTTConnection() bool CMQTTConnection::open() { char name[50U]; -#if defined(_WIN32) || defined(_WIN64) - ::sprintf(name, "DStarRepeater.%u", (unsigned)::_getpid()); -#else - ::sprintf(name, "DStarRepeater.%u", (unsigned)::getpid()); -#endif + ::snprintf(name, sizeof(name), "DStarRepeater.%u", (unsigned)::getpid()); ::fprintf(stdout, "DStarRepeater (%s) connecting to MQTT as %s\n", m_name.c_str(), name); @@ -75,7 +71,7 @@ bool CMQTTConnection::open() } if (m_authEnabled) - ::mosquitto_username_pw_set(m_mosq, m_username.c_str(), m_password.c_str()); + ::mosquitto_username_pw_set(m_mosq, m_username.c_str(), m_password.c_str()); ::mosquitto_connect_callback_set(m_mosq, onConnect); ::mosquitto_subscribe_callback_set(m_mosq, onSubscribe); @@ -127,7 +123,7 @@ bool CMQTTConnection::publish(const char* topic, const unsigned char* data, unsi if (::strchr(topic, '/') == nullptr) { char topicEx[100U]; - ::sprintf(topicEx, "%s/%s", m_name.c_str(), topic); + ::snprintf(topicEx, sizeof(topicEx), "%s/%s", m_name.c_str(), topic); int rc = ::mosquitto_publish(m_mosq, nullptr, topicEx, len, data, static_cast(m_qos), false); if (rc != MOSQ_ERR_SUCCESS) { @@ -174,7 +170,7 @@ void CMQTTConnection::onConnect(mosquitto* mosq, void* obj, int rc) if (topic.find_first_of('/') == std::string::npos) { char topicEx[100U]; - ::sprintf(topicEx, "%s/%s", p->m_name.c_str(), topic.c_str()); + ::snprintf(topicEx, sizeof(topicEx), "%s/%s", p->m_name.c_str(), topic.c_str()); rc = ::mosquitto_subscribe(mosq, nullptr, topicEx, static_cast(p->m_qos)); if (rc != MOSQ_ERR_SUCCESS) { @@ -213,7 +209,7 @@ void CMQTTConnection::onMessage(mosquitto* mosq, void* obj, const mosquitto_mess std::string topic = (*it).first; char topicEx[100U]; - ::sprintf(topicEx, "%s/%s", p->m_name.c_str(), topic.c_str()); + ::snprintf(topicEx, sizeof(topicEx), "%s/%s", p->m_name.c_str(), topic.c_str()); if (::strcmp(topicEx, message->topic) == 0) { (*it).second((unsigned char*)message->payload, message->payloadlen); diff --git a/Common/MQTTConnection.h b/Common/MQTTConnection.h index 1c21188..9d3f0c0 100644 --- a/Common/MQTTConnection.h +++ b/Common/MQTTConnection.h @@ -23,9 +23,27 @@ #include +#include #include #include +/* + * Thin wrapper around the libmosquitto client for publish-only MQTT telemetry. + * + * open() initialises the mosquitto client, registers callbacks, and starts the + * internal mosquitto network loop thread (mosquitto_loop_start). After open() + * returns, publish() can be called from any thread; mosquitto handles its own + * thread safety internally. + * + * Topics without a '/' are automatically prefixed with the configured name + * (e.g. "log" becomes "MyRepeater/log"). Topics containing '/' are used as-is. + * + * Optional subscriptions can be provided via the subs vector; each entry pairs + * a topic string with a callback function invoked on incoming messages. + * + * This class is compiled only when MQTT=1 is passed to make. + */ + enum class MQTT_QOS : int { AT_MODE_ONCE = 0, AT_LEAST_ONCE = 1, @@ -48,7 +66,7 @@ public: private: std::string m_host; unsigned short m_port; - std::string m_name; + std::string m_name; // Prefix prepended to bare topic strings. bool m_authEnabled; std::string m_username; std::string m_password; @@ -56,8 +74,9 @@ private: unsigned int m_keepalive; MQTT_QOS m_qos; mosquitto* m_mosq; - bool m_connected; + std::atomic m_connected; // Set true in onConnect, false in onDisconnect. + // Mosquitto event callbacks (called on the internal mosquitto thread). static void onConnect(mosquitto* mosq, void* obj, int rc); static void onSubscribe(mosquitto* mosq, void* obj, int mid, int qosCount, const int* grantedQOS); static void onMessage(mosquitto* mosq, void* obj, const mosquitto_message* message); diff --git a/Common/MQTTPublisher.h b/Common/MQTTPublisher.h index 2001586..73590ee 100644 --- a/Common/MQTTPublisher.h +++ b/Common/MQTTPublisher.h @@ -22,8 +22,14 @@ #if defined(MQTT) /* - * Display-Driver-compatible MQTT JSON publishing for DStarRepeater. + * Inline helper functions that format and publish D-Star events as JSON to the + * MQTT broker via the global g_mqtt (CMQTTConnection*) instance. * + * All helpers are no-ops when g_mqtt is nullptr (MQTT not configured) or when + * the broker is not connected. They publish to the "json" sub-topic, which is + * automatically prefixed with the configured repeater name by CMQTTConnection. + * + * Display-Driver-compatible MQTT JSON publishing for DStarRepeater: * Publishes event-driven JSON messages to the "json" topic in the format * expected by Display-Driver, enabling LCD/OLED display output. * @@ -35,27 +41,55 @@ #include "MQTTConnection.h" #include "Logger.h" -#include - +#include #include +// Escape a string for safe embedding in a JSON value. +// Handles the characters that would break JSON string syntax or silently +// corrupt the document: backslash, double-quote, and the common C0 controls. +// Non-printable control characters other than \n/\r/\t are dropped entirely +// so that malformed radio headers cannot produce invalid JSON. +static inline std::string jsonEscape(const std::string& s) +{ + std::string out; + out.reserve(s.size()); + for (char c : s) { + if (c == '"') out += "\\\""; + else if (c == '\\') out += "\\\\"; + else if (c == '\n') out += "\\n"; + else if (c == '\r') out += "\\r"; + else if (c == '\t') out += "\\t"; + else if (c >= 0x20) out += c; + // drop other non-printable control characters + } + return out; +} + // Publish a D-Star call start event (RF or network) -static inline void mqttPublishDStarStart(const wxString& myCall1, const wxString& myCall2, - const wxString& yourCall, const wxString& rptCall2, const char* source) +static inline void mqttPublishDStarStart(const std::string& myCall1, const std::string& myCall2, + const std::string& yourCall, const std::string& rptCall2, const char* source) { - if (g_mqtt == NULL) + if (g_mqtt == nullptr) return; + // Escape all callsign strings: over-the-air headers are untrusted input + // and may contain characters (e.g. '"' or '\') that break JSON syntax. + // 'source' is a string literal supplied by our own code, not radio data. + const std::string cs1 = jsonEscape(myCall1); + const std::string cs2 = jsonEscape(myCall2); + const std::string yc = jsonEscape(yourCall); + const std::string rc2 = jsonEscape(rptCall2); + char buf[512]; ::snprintf(buf, sizeof(buf), "{\"D-Star\":{\"action\":\"start\"," "\"source_cs\":\"%s\",\"source_ext\":\"%s\"," "\"destination_cs\":\"%s\",\"reflector\":\"%s\"," "\"source\":\"%s\"}}", - (const char*)myCall1.mb_str(), - (const char*)myCall2.mb_str(), - (const char*)yourCall.mb_str(), - (const char*)rptCall2.mb_str(), + cs1.c_str(), + cs2.c_str(), + yc.c_str(), + rc2.c_str(), source); g_mqtt->publish("json", buf); @@ -64,7 +98,7 @@ static inline void mqttPublishDStarStart(const wxString& myCall1, const wxString // Publish a D-Star call end event (normal termination) static inline void mqttPublishDStarEnd() { - if (g_mqtt == NULL) + if (g_mqtt == nullptr) return; g_mqtt->publish("json", "{\"D-Star\":{\"action\":\"end\"}}"); @@ -73,7 +107,7 @@ static inline void mqttPublishDStarEnd() // Publish a D-Star call lost event (watchdog timeout / abnormal) static inline void mqttPublishDStarLost() { - if (g_mqtt == NULL) + if (g_mqtt == nullptr) return; g_mqtt->publish("json", "{\"D-Star\":{\"action\":\"lost\"}}"); @@ -82,16 +116,30 @@ static inline void mqttPublishDStarLost() // Publish idle mode (no traffic) static inline void mqttPublishIdle() { - if (g_mqtt == NULL) + if (g_mqtt == nullptr) return; g_mqtt->publish("json", "{\"MMDVM\":{\"mode\":\"idle\"}}"); } +// Publish D-Star RSSI value (DVAP signal strength in dBm) +static inline void mqttPublishRSSI(int rssi) +{ + if (g_mqtt == nullptr) + return; + + char buf[128]; + ::snprintf(buf, sizeof(buf), + "{\"RSSI\":{\"mode\":\"D-Star\",\"value\":%d}}", + rssi); + + g_mqtt->publish("json", buf); +} + // Publish D-Star BER value static inline void mqttPublishBER(float ber) { - if (g_mqtt == NULL) + if (g_mqtt == nullptr) return; char buf[128]; @@ -103,15 +151,19 @@ static inline void mqttPublishBER(float ber) } // Publish D-Star slow data text -static inline void mqttPublishText(const wxString& text) +static inline void mqttPublishText(const std::string& text) { - if (g_mqtt == NULL) + if (g_mqtt == nullptr) return; + // Slow-data text arrives over the air and is untrusted; escape it so + // that a crafted transmission cannot produce malformed JSON. + const std::string escaped = jsonEscape(text); + char buf[256]; ::snprintf(buf, sizeof(buf), "{\"Text\":{\"mode\":\"D-Star\",\"value\":\"%s\"}}", - (const char*)text.mb_str()); + escaped.c_str()); g_mqtt->publish("json", buf); } diff --git a/Common/Makefile b/Common/Makefile index efba53f..8da1b39 100644 --- a/Common/Makefile +++ b/Common/Makefile @@ -2,7 +2,7 @@ OBJECTS = AMBEFEC.o AnnouncementUnit.o ArduinoController.o BeaconUnit.o Callsign DStarGMSKDemodulator.o DStarGMSKModulator.o DStarRepeaterConfig.o DStarScrambler.o DummyController.o DVAPController.o \ DVMegaController.o DVRPTRV1Controller.o DVRPTRV2Controller.o DVRPTRV3Controller.o DVTOOLFileReader.o DVTOOLFileWriter.o \ ExternalController.o FIRFilter.o GatewayProtocolHandler.o GMSKController.o GMSKModem.o GMSKModemLibUsb.o Golay.o \ - GPIOController.o HardwareController.o HeaderData.o IcomController.o K8055Controller.o LogEvent.o Logger.o MMDVMController.o \ + GPIOController.o HardwareController.o HeaderData.o IcomController.o K8055Controller.o Logger.o MMDVMController.o \ Modem.o OutputQueue.o RepeaterProtocolHandler.o SerialDataController.o SerialLineController.o SerialPortSelector.o \ SlowDataDecoder.o SlowDataEncoder.o SoundCardController.o SoundCardReaderWriter.o SplitController.o TCPReaderWriter.o \ MQTTConnection.o Timer.o UDPReaderWriter.o UDRCController.o URIUSBController.o Utils.o @@ -16,10 +16,15 @@ Common.a: $(OBJECTS) -include $(OBJECTS:.o=.d) +# BeaconUnit needs DATA_DIR at compile time to locate AMBE voice files in the +# system data directory when they are not found under $HOME. +BeaconUnit.o: BeaconUnit.cpp + $(CXX) $(CFLAGS) -DDATA_DIR='"$(DATADIR)"' -c -o $@ $< + $(CXX) -MM $(CFLAGS) -DDATA_DIR='"$(DATADIR)"' $< > $*.d + %.o: %.cpp - $(CXX) -DwxUSE_GUI=0 $(CFLAGS) -c -o $@ $< - $(CXX) -MM -DwxUSE_GUI=0 $(CFLAGS) $< > $*.d + $(CXX) $(CFLAGS) -c -o $@ $< + $(CXX) -MM $(CFLAGS) $< > $*.d clean: $(RM) Common.a *.o *.d *.bak *~ - diff --git a/Common/Modem.cpp b/Common/Modem.cpp index 524e15d..28d9fc4 100644 --- a/Common/Modem.cpp +++ b/Common/Modem.cpp @@ -19,17 +19,19 @@ #include "DStarDefines.h" #include "Modem.h" +// Internal scratch buffer used by read() to hold one decoded message payload. +// 200 bytes is large enough for RADIO_HEADER_LENGTH_BYTES (41) and a max DV frame. const unsigned int BUFFER_LENGTH = 200U; CModem::CModem() : -wxThread(wxTHREAD_JOINABLE), m_rxData(1000U), m_mutex(), m_tx(false), m_stopped(false), +m_thread(), m_readType(DSMTT_NONE), m_readLength(0U), -m_readBuffer(NULL) +m_readBuffer(nullptr) { m_readBuffer = new unsigned char[BUFFER_LENGTH]; } @@ -51,8 +53,9 @@ DSMT_TYPE CModem::read() if (m_rxData.isEmpty()) return DSMTT_NONE; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); + // Each ring-buffer message is framed as [type:1][length:1][payload:length]. unsigned char hdr[2U]; m_rxData.getData(hdr, 2U); @@ -66,7 +69,7 @@ DSMT_TYPE CModem::read() CHeaderData* CModem::readHeader() { if (m_readType != DSMTT_HEADER) - return NULL; + return nullptr; return new CHeaderData(m_readBuffer, RADIO_HEADER_LENGTH_BYTES, false); } @@ -89,6 +92,6 @@ void CModem::stop() { m_stopped = true; - Wait(); + if (m_thread.joinable()) + m_thread.join(); } - diff --git a/Common/Modem.h b/Common/Modem.h index 2351367..22f9151 100644 --- a/Common/Modem.h +++ b/Common/Modem.h @@ -16,50 +16,93 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* + * CModem is the abstract base class for all hardware modem drivers + * (DVAP, MMDVM, DVMega, GMSK USB, etc.). + * + * Threading model + * --------------- + * Each concrete driver runs its own I/O thread (started by start(), stopped by + * stop()). That thread reads received frames from the hardware and pushes them + * into the m_rxData ring buffer. The repeater thread then calls read() / + * readHeader() / readData() to drain the buffer. The mutex guards the ring + * buffer against concurrent access between the two threads. + * + * Read protocol + * ------------- + * The repeater thread calls read() each tick. If a message is available, + * read() returns the DSMT_TYPE tag and latches the payload into an internal + * scratch buffer. The caller then calls the matching accessor: + * - DSMTT_HEADER -> readHeader() (returns a heap-allocated CHeaderData) + * - DSMTT_DATA -> readData() (copies bytes into the caller's buffer) + * Other types (START, EOT, LOST) carry no payload and need no further call. + */ + #ifndef Modem_H #define Modem_H #include "HeaderData.h" #include "RingBuffer.h" +#include "StdCompat.h" -#include +#include +#include +#include +// Message type tags pushed by the driver thread into the ring buffer. +// Each message is prefixed with [type:1][length:1][payload:length] bytes. enum DSMT_TYPE { - DSMTT_NONE, - DSMTT_START, - DSMTT_HEADER, - DSMTT_DATA, - DSMTT_EOT, - DSMTT_LOST + DSMTT_NONE, // No message waiting + DSMTT_START, // Hardware has started (carrier / sync detected) + DSMTT_HEADER, // A valid D-Star header has been decoded from the air + DSMTT_DATA, // One DV frame of voice + slow-data payload + DSMTT_EOT, // End-of-transmission detected on air + DSMTT_LOST, // Signal lost mid-frame (carrier dropped unexpectedly) }; -class CModem : public wxThread { +class CModem { public: CModem(); virtual ~CModem(); + // Start the driver I/O thread and open the hardware device. virtual bool start() = 0; + // Transmit a D-Star header (called before the first writeData()). virtual bool writeHeader(const CHeaderData& header) = 0; + // Transmit one DV frame; set end=true for the final frame of a transmission. virtual bool writeData(const unsigned char* data, unsigned int length, bool end) = 0; + // Number of DV frames the TX buffer can still accept. virtual unsigned int getSpace() = 0; + // True when the hardware is ready to accept TX frames. virtual bool isTXReady() = 0; + // True while the hardware is actively transmitting. virtual bool isTX(); + // Drain the next message from the ring buffer; returns its type tag. + // Must be called before readHeader() or readData(). virtual DSMT_TYPE read(); + // Returns the header decoded by the most recent read() == DSMTT_HEADER call. + // Caller owns the returned object. virtual CHeaderData* readHeader(); + // Copies DV frame bytes from the most recent read() == DSMTT_DATA call. virtual unsigned int readData(unsigned char* data, unsigned int length); + // Signal the driver thread to exit and wait for it to join. virtual void stop(); protected: + // SPSC ring buffer: driver thread writes, repeater thread reads. CRingBuffer m_rxData; - wxMutex m_mutex; - bool m_tx; - bool m_stopped; + // Guards m_rxData against concurrent access. + std::mutex m_mutex; + std::atomic m_tx; // Current TX state of the hardware + std::atomic m_stopped; // Set by stop() to terminate the thread + std::thread m_thread; // The driver I/O thread private: + // Scratch state populated by read() and consumed by readHeader()/readData(). DSMT_TYPE m_readType; unsigned int m_readLength; unsigned char* m_readBuffer; diff --git a/Common/OutputQueue.cpp b/Common/OutputQueue.cpp index c1da6c7..44dfee2 100644 --- a/Common/OutputQueue.cpp +++ b/Common/OutputQueue.cpp @@ -18,14 +18,16 @@ #include "OutputQueue.h" +#include + COutputQueue::COutputQueue(unsigned int space, unsigned int threshold) : m_data(space), m_threshold(threshold), -m_header(NULL), +m_header(nullptr), m_count(0U) { - wxASSERT(space > 0U); - wxASSERT(threshold > 0U); + assert(space > 0U); + assert(threshold > 0U); } COutputQueue::~COutputQueue() @@ -35,7 +37,7 @@ COutputQueue::~COutputQueue() void COutputQueue::setHeader(CHeaderData* header) { - wxASSERT(header != NULL); + assert(header != nullptr); delete m_header; @@ -46,14 +48,14 @@ CHeaderData* COutputQueue::getHeader() { CHeaderData* header = m_header; - m_header = NULL; + m_header = nullptr; return header; } unsigned int COutputQueue::getData(unsigned char *data, unsigned int length, bool& end) { - wxASSERT(data != NULL); + assert(data != nullptr); if (m_data.isEmpty()) { end = false; @@ -66,8 +68,6 @@ unsigned int COutputQueue::getData(unsigned char *data, unsigned int length, boo end = hdr[0U] == 1U; if (length < hdr[1U]) { - wxLogWarning(wxT("Output buffer is too short, %u < %u"), length, hdr[1U]); - unsigned int len = m_data.getData(data, length); // Purge the excess data @@ -84,11 +84,10 @@ unsigned int COutputQueue::getData(unsigned char *data, unsigned int length, boo unsigned int COutputQueue::addData(const unsigned char *data, unsigned int length, bool end) { - wxASSERT(data != NULL); + assert(data != nullptr); bool ret = m_data.hasSpace(length + 2U); if (!ret) { - // XXX wxLogWarning(wxT("Not enough space in the output queue")); return 0U; } @@ -107,17 +106,17 @@ unsigned int COutputQueue::addData(const unsigned char *data, unsigned int lengt bool COutputQueue::headerReady() const { - return m_header != NULL && m_count >= m_threshold; + return m_header != nullptr && m_count >= m_threshold; } bool COutputQueue::dataReady() const { - return m_header == NULL && m_count >= m_threshold; + return m_header == nullptr && m_count >= m_threshold; } bool COutputQueue::isEmpty() { - return m_header == NULL && m_data.isEmpty(); + return m_header == nullptr && m_data.isEmpty(); } void COutputQueue::reset() @@ -125,14 +124,14 @@ void COutputQueue::reset() m_data.clear(); delete m_header; - m_header = NULL; + m_header = nullptr; m_count = 0U; } void COutputQueue::setThreshold(unsigned int threshold) { - wxASSERT(threshold > 0U); + assert(threshold > 0U); m_threshold = threshold; } diff --git a/Common/OutputQueue.h b/Common/OutputQueue.h index f4f5e8b..9a1537a 100644 --- a/Common/OutputQueue.h +++ b/Common/OutputQueue.h @@ -22,20 +22,39 @@ #include "RingBuffer.h" #include "HeaderData.h" -#include - +/* + * FIFO queue for outbound D-Star frames with type/length/data encoding. + * + * Each frame is stored in the ring buffer as a 2-byte header followed by the + * frame payload: + * byte 0: end flag — 1 if this is the last frame of the transmission, else 0 + * byte 1: payload length in bytes + * [payload bytes] + * + * A separate m_header pointer holds the pending CHeaderData for the next + * transmission. Playback is gated by a threshold: neither the header nor data + * frames are released to the transmitter until m_count >= m_threshold, which + * ensures there is a small buffer of frames pre-queued before TX begins (to + * absorb jitter in frame delivery from the network). Receiving an end-of- + * stream frame immediately raises the count to the threshold so the remaining + * frames drain promptly. + */ class COutputQueue { public: COutputQueue(unsigned int space, unsigned int threshold); ~COutputQueue(); + // Takes ownership of header; replaces any previously pending header. void setHeader(CHeaderData* header); + // Returns the pending header and relinquishes ownership (caller must delete). CHeaderData* getHeader(); unsigned int getData(unsigned char* data, unsigned int length, bool& end); unsigned int addData(const unsigned char* data, unsigned int length, bool end); + // True when a header is queued and the pre-fill threshold has been reached. bool headerReady() const; + // True when data frames are queued and no header is pending (mid-stream). bool dataReady() const; bool isEmpty(); @@ -48,7 +67,7 @@ private: CRingBuffer m_data; unsigned int m_threshold; CHeaderData* m_header; - unsigned int m_count; + unsigned int m_count; // Frames added since the last header; triggers playback start. }; #endif diff --git a/Common/RepeaterProtocolHandler.cpp b/Common/RepeaterProtocolHandler.cpp index 2e9e963..8a5fd75 100644 --- a/Common/RepeaterProtocolHandler.cpp +++ b/Common/RepeaterProtocolHandler.cpp @@ -21,11 +21,12 @@ #include "DStarDefines.h" #include "Utils.h" +// Uncomment to hex-dump every outbound DSRP packet to stdout for debugging. // #define DUMP_TX const unsigned int BUFFER_LENGTH = 255U; -CRepeaterProtocolHandler::CRepeaterProtocolHandler(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name) : +CRepeaterProtocolHandler::CRepeaterProtocolHandler(const std::string& gatewayAddress, unsigned int gatewayPort, const std::string& localAddress, unsigned int localPort, const std::string& name) : m_socket(localAddress, localPort), m_address(), m_port(gatewayPort), @@ -34,15 +35,14 @@ m_outId(0U), m_outSeq(0U), m_type(NETWORK_NONE), m_inId(0U), -m_buffer(NULL), +m_buffer(nullptr), m_length(0U) { m_address = CUDPReaderWriter::lookup(gatewayAddress); m_buffer = new unsigned char[BUFFER_LENGTH]; - wxDateTime now = wxDateTime::UNow(); - ::srand(now.GetMillisecond()); + ::srand((unsigned int)::time(nullptr)); } CRepeaterProtocolHandler::~CRepeaterProtocolHandler() @@ -82,19 +82,19 @@ bool CRepeaterProtocolHandler::writeHeader(const CHeaderData& header) buffer[10] = header.getFlag3(); for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[11 + i] = header.getRptCall1().GetChar(i); + buffer[11 + i] = header.getRptCall1()[i]; for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[19 + i] = header.getRptCall2().GetChar(i); + buffer[19 + i] = header.getRptCall2()[i]; for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[27 + i] = header.getYourCall().GetChar(i); + buffer[27 + i] = header.getYourCall()[i]; for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[35 + i] = header.getMyCall1().GetChar(i); + buffer[35 + i] = header.getMyCall1()[i]; for (unsigned int i = 0U; i < SHORT_CALLSIGN_LENGTH; i++) - buffer[43 + i] = header.getMyCall2().GetChar(i); + buffer[43 + i] = header.getMyCall2()[i]; // Get the checksum for the header CCCITTChecksumReverse csum; @@ -104,7 +104,7 @@ bool CRepeaterProtocolHandler::writeHeader(const CHeaderData& header) m_outSeq = 0U; #if defined(DUMP_TX) - CUtils::dump(wxT("Sending Header"), buffer, 49U); + CUtils::dump("Sending Header", buffer, 49U); #endif for (unsigned int i = 0U; i < 2U; i++) { @@ -118,8 +118,8 @@ bool CRepeaterProtocolHandler::writeHeader(const CHeaderData& header) bool CRepeaterProtocolHandler::writeData(const unsigned char* data, unsigned int length, unsigned int errors, bool end) { - wxASSERT(data != NULL); - wxASSERT(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES); + assert(data != nullptr); + assert(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES); unsigned char buffer[30U]; @@ -150,7 +150,7 @@ bool CRepeaterProtocolHandler::writeData(const unsigned char* data, unsigned int ::memcpy(buffer + 9U, data, length); #if defined(DUMP_TX) - CUtils::dump(wxT("Sending Data"), buffer, length + 9U); + CUtils::dump("Sending Data", buffer, length + 9U); #endif return m_socket.write(buffer, length + 9U, m_address, m_port); @@ -180,19 +180,19 @@ bool CRepeaterProtocolHandler::writeBusyHeader(const CHeaderData& header) buffer[10] = header.getFlag3(); for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[11 + i] = header.getRptCall1().GetChar(i); + buffer[11 + i] = header.getRptCall1()[i]; for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[19 + i] = header.getRptCall2().GetChar(i); + buffer[19 + i] = header.getRptCall2()[i]; for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[27 + i] = header.getYourCall().GetChar(i); + buffer[27 + i] = header.getYourCall()[i]; for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++) - buffer[35 + i] = header.getMyCall1().GetChar(i); + buffer[35 + i] = header.getMyCall1()[i]; for (unsigned int i = 0U; i < SHORT_CALLSIGN_LENGTH; i++) - buffer[43 + i] = header.getMyCall2().GetChar(i); + buffer[43 + i] = header.getMyCall2()[i]; // Get the checksum for the header CCCITTChecksumReverse csum; @@ -202,7 +202,7 @@ bool CRepeaterProtocolHandler::writeBusyHeader(const CHeaderData& header) m_outSeq = 0U; #if defined(DUMP_TX) - CUtils::dump(wxT("Sending Busy Header"), buffer, 49U); + CUtils::dump("Sending Busy Header", buffer, 49U); #endif return m_socket.write(buffer, 49U, m_address, m_port); @@ -210,8 +210,8 @@ bool CRepeaterProtocolHandler::writeBusyHeader(const CHeaderData& header) bool CRepeaterProtocolHandler::writeBusyData(const unsigned char* data, unsigned int length, unsigned int errors, bool end) { - wxASSERT(data != NULL); - wxASSERT(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES); + assert(data != nullptr); + assert(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES); unsigned char buffer[30U]; @@ -242,13 +242,13 @@ bool CRepeaterProtocolHandler::writeBusyData(const unsigned char* data, unsigned ::memcpy(buffer + 9U, data, length); #if defined(DUMP_TX) - CUtils::dump(wxT("Sending Busy Data"), buffer, length + 9U); + CUtils::dump("Sending Busy Data", buffer, length + 9U); #endif return m_socket.write(buffer, length + 9U, m_address, m_port); } -bool CRepeaterProtocolHandler::writePoll(const wxString& text) +bool CRepeaterProtocolHandler::writePoll(const std::string& text) { unsigned char buffer[40U]; @@ -259,15 +259,16 @@ bool CRepeaterProtocolHandler::writePoll(const wxString& text) buffer[4] = 0x0A; // Poll with text - unsigned int length = text.Length(); + unsigned int length = text.length(); + if (length > 34U) length = 34U; // buffer[40] - offset[5] - null[1] for (unsigned int i = 0U; i < length; i++) - buffer[5U + i] = text.GetChar(i); + buffer[5U + i] = text[i]; buffer[5U + length] = 0x00; #if defined(DUMP_TX) - CUtils::dump(wxT("Sending Poll"), buffer, 6U + length); + CUtils::dump("Sending Poll", buffer, 6U + length); #endif return m_socket.write(buffer, 6U + length, m_address, m_port); @@ -284,15 +285,16 @@ bool CRepeaterProtocolHandler::writeRegister() buffer[4] = 0x0B; // Register with name - unsigned int length = m_name.Length(); + unsigned int length = m_name.length(); + if (length > 34U) length = 34U; // buffer[40] - offset[5] - null[1] for (unsigned int i = 0U; i < length; i++) - buffer[5U + i] = m_name.GetChar(i); + buffer[5U + i] = m_name[i]; buffer[5U + length] = 0x00; #if defined(DUMP_TX) - CUtils::dump(wxT("Sending Register"), buffer, 6U + length); + CUtils::dump("Sending Register", buffer, 6U + length); #endif return m_socket.write(buffer, 6U + length, m_address, m_port); @@ -320,10 +322,12 @@ bool CRepeaterProtocolHandler::readPackets() if (length <= 0) return false; - // Check if the data is for us + // Validate the source IP and port against the configured gateway address. + // Any packet arriving from a different host is silently rejected to prevent + // a third party from injecting audio or control packets into the repeater. if (m_address.s_addr != address.s_addr || m_port != port) { - wxLogMessage(wxT("Packet received from an invalid source, %08X != %08X and/or %u != %u"), m_address.s_addr, address.s_addr, m_port, port); - CUtils::dump(wxT("Data"), m_buffer, length); + ::fprintf(stderr, "Packet received from an invalid source, %08X != %08X and/or %u != %u\n", m_address.s_addr, address.s_addr, m_port, port); + CUtils::dump("Data", m_buffer, length); return false; } @@ -373,7 +377,7 @@ bool CRepeaterProtocolHandler::readPackets() // Header data else if (m_buffer[4] == 0x20U) { - wxUint16 id = m_buffer[5] * 256U + m_buffer[6]; + uint16_t id = m_buffer[5] * 256U + m_buffer[6]; // Are we listening for headers? if (m_inId != 0U) @@ -386,7 +390,7 @@ bool CRepeaterProtocolHandler::readPackets() // User data else if (m_buffer[4] == 0x21U) { - wxUint16 id = m_buffer[5] * 256U + m_buffer[6]; + uint16_t id = m_buffer[5] * 256U + m_buffer[6]; // Check that the stream id matches the valid header, reject otherwise if (id != m_inId) @@ -405,7 +409,7 @@ bool CRepeaterProtocolHandler::readPackets() } } - CUtils::dump(wxT("Unknown packet from the Gateway"), m_buffer, m_length); + CUtils::dump("Unknown packet from the Gateway", m_buffer, m_length); return true; } @@ -413,7 +417,7 @@ bool CRepeaterProtocolHandler::readPackets() CHeaderData* CRepeaterProtocolHandler::readHeader() { if (m_type != NETWORK_HEADER) - return NULL; + return nullptr; // If the checksum is 0xFFFF then we accept the header without testing the checksum if (m_buffer[47U] == 0xFFU && m_buffer[48U] == 0xFFU) @@ -423,9 +427,9 @@ CHeaderData* CRepeaterProtocolHandler::readHeader() CHeaderData* header = new CHeaderData(m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES, true); if (!header->isValid()) { - CUtils::dump(wxT("Header checksum failure from the Gateway"), m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES); + CUtils::dump("Header checksum failure from the Gateway", m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES); delete header; - return NULL; + return nullptr; } return header; @@ -436,6 +440,9 @@ unsigned int CRepeaterProtocolHandler::readData(unsigned char* buffer, unsigned if (m_type != NETWORK_DATA) return 0U; + if (m_length < 9U) + return 0U; + unsigned int dataLen = m_length - 9U; // Is our buffer too small? @@ -462,70 +469,70 @@ unsigned int CRepeaterProtocolHandler::readData(unsigned char* buffer, unsigned return dataLen; } -void CRepeaterProtocolHandler::readText(wxString& text, LINK_STATUS& status, wxString& reflector) +void CRepeaterProtocolHandler::readText(std::string& text, LINK_STATUS& status, std::string& reflector) { if (m_type != NETWORK_TEXT) { - text = wxT(" "); - reflector = wxT(" "); + text = " "; + reflector = " "; status = LS_NONE; return; } - text = wxString((char*)(m_buffer + 5U), wxConvLocal, 20U); + text = std::string((char*)(m_buffer + 5U), 20U); status = LINK_STATUS(m_buffer[25U]); - reflector = wxString((char*)(m_buffer + 26U), wxConvLocal, 8U); + reflector = std::string((char*)(m_buffer + 26U), 8U); } -void CRepeaterProtocolHandler::readTempText(wxString& text) +void CRepeaterProtocolHandler::readTempText(std::string& text) { if (m_type != NETWORK_TEMPTEXT) { - text = wxT(" "); + text = " "; return; } - text = wxString((char*)(m_buffer + 5U), wxConvLocal, 20U); + text = std::string((char*)(m_buffer + 5U), 20U); } -wxString CRepeaterProtocolHandler::readStatus1() +std::string CRepeaterProtocolHandler::readStatus1() { if (m_type != NETWORK_STATUS1) - return wxEmptyString; + return std::string(); - return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U); + return std::string((char*)(m_buffer + 6U), 20U); } -wxString CRepeaterProtocolHandler::readStatus2() +std::string CRepeaterProtocolHandler::readStatus2() { if (m_type != NETWORK_STATUS2) - return wxEmptyString; + return std::string(); - return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U); + return std::string((char*)(m_buffer + 6U), 20U); } -wxString CRepeaterProtocolHandler::readStatus3() +std::string CRepeaterProtocolHandler::readStatus3() { if (m_type != NETWORK_STATUS3) - return wxEmptyString; + return std::string(); - return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U); + return std::string((char*)(m_buffer + 6U), 20U); } -wxString CRepeaterProtocolHandler::readStatus4() +std::string CRepeaterProtocolHandler::readStatus4() { if (m_type != NETWORK_STATUS4) - return wxEmptyString; + return std::string(); - return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U); + return std::string((char*)(m_buffer + 6U), 20U); } -wxString CRepeaterProtocolHandler::readStatus5() +std::string CRepeaterProtocolHandler::readStatus5() { if (m_type != NETWORK_STATUS5) - return wxEmptyString; + return std::string(); - return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U); + return std::string((char*)(m_buffer + 6U), 20U); } void CRepeaterProtocolHandler::reset() diff --git a/Common/RepeaterProtocolHandler.h b/Common/RepeaterProtocolHandler.h index 1a74cac..13090f8 100644 --- a/Common/RepeaterProtocolHandler.h +++ b/Common/RepeaterProtocolHandler.h @@ -22,50 +22,70 @@ #include "UDPReaderWriter.h" #include "DStarDefines.h" #include "HeaderData.h" +#include "StdCompat.h" -#include - +/* + * Implements the D-Star Repeater Protocol (DSRP) from the repeater side. + * + * DSRP is the UDP-based protocol used between the repeater daemon and + * ircDDBGateway/DStarGateway. All packets begin with the four-byte magic + * "DSRP", followed by a one-byte type code: + * + * 0x0A Poll — keepalive with a human-readable text string + * 0x0B Register — sent on startup to name this repeater to the gateway + * 0x20 Header — D-Star radio header (callsigns + flags) + * 0x21 Data — one DV frame (voice + slow-data) + * 0x22 Busy Header — header received while repeater is already busy + * 0x23 Busy Data — data received while repeater is already busy + * + * The repeater daemon calls write*() to push outbound audio toward the + * gateway, and calls read() each timer tick to drain inbound packets. + */ class CRepeaterProtocolHandler { public: - CRepeaterProtocolHandler(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name); + CRepeaterProtocolHandler(const std::string& gatewayAddress, unsigned int gatewayPort, const std::string& localAddress, unsigned int localPort, const std::string& name); ~CRepeaterProtocolHandler(); bool open(); + // Sends the header packet twice to reduce the impact of UDP packet loss. bool writeHeader(const CHeaderData& header); bool writeBusyHeader(const CHeaderData& header); bool writeData(const unsigned char* data, unsigned int length, unsigned int errors, bool end); bool writeBusyData(const unsigned char* data, unsigned int length, unsigned int errors, bool end); - bool writePoll(const wxString& text); + bool writePoll(const std::string& text); bool writeRegister(); + // Drains all pending UDP datagrams; returns the type of the last useful one. NETWORK_TYPE read(); - void readText(wxString& text, LINK_STATUS& status, wxString& reflector); - void readTempText(wxString& text); - wxString readStatus1(); - wxString readStatus2(); - wxString readStatus3(); - wxString readStatus4(); - wxString readStatus5(); + void readText(std::string& text, LINK_STATUS& status, std::string& reflector); + void readTempText(std::string& text); + std::string readStatus1(); + std::string readStatus2(); + std::string readStatus3(); + std::string readStatus4(); + std::string readStatus5(); CHeaderData* readHeader(); unsigned int readData(unsigned char* data, unsigned int length, unsigned char& seqNo); + // Clears the current inbound session ID so a new stream can be accepted. void reset(); void close(); private: CUDPReaderWriter m_socket; - in_addr m_address; + in_addr m_address; // Resolved gateway address; packets from any other source are dropped. unsigned int m_port; - wxString m_name; - wxUint16 m_outId; - wxUint8 m_outSeq; + std::string m_name; + uint16_t m_outId; // Random session ID assigned at the start of each transmission. + uint8_t m_outSeq; // Per-frame sequence number (0–20, resets on each sync frame). NETWORK_TYPE m_type; - wxUint16 m_inId; + uint16_t m_inId; // Session ID of the stream currently being received (0 = idle). unsigned char* m_buffer; unsigned int m_length; + // Reads one UDP datagram and sets m_type; returns true if more may be waiting. bool readPackets(); }; diff --git a/Common/RingBuffer.h b/Common/RingBuffer.h index 3c40af0..fb8094b 100644 --- a/Common/RingBuffer.h +++ b/Common/RingBuffer.h @@ -16,20 +16,40 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* + * Single-producer / single-consumer (SPSC) lock-free ring buffer. + * + * Used primarily for modem-to-repeater data transfer: the modem driver thread + * writes received frames via addData(), while the repeater thread reads them + * via getData() or peek(). The atomic m_iPtr (write index) and m_oPtr (read + * index) make the empty/full checks safe across exactly two threads without a + * mutex, provided each pointer is only advanced by its respective thread. + * + * Important: this is NOT safe for multiple concurrent producers or consumers. + * CModem wraps accesses with a mutex when both threads need stronger ordering + * guarantees (e.g. when writing the 2-byte framing header atomically with the + * payload). + * + * The buffer wastes one slot so that iPtr == oPtr always means empty rather + * than ambiguously full. Capacity is therefore (length - 1) elements. + */ + #ifndef RingBuffer_H #define RingBuffer_H -#include +#include +#include +#include template class CRingBuffer { public: CRingBuffer(unsigned int length) : m_length(length), - m_buffer(NULL), + m_buffer(nullptr), m_iPtr(0U), m_oPtr(0U) { - wxASSERT(length > 0U); + assert(length > 0U); m_buffer = new T[length]; @@ -41,6 +61,8 @@ public: delete[] m_buffer; } + // Write nSamples elements from buffer. Returns 0 if there is not enough + // free space; the caller must retry or discard. unsigned int addData(const T* buffer, unsigned int nSamples) { if (nSamples > freeSpace()) @@ -56,6 +78,8 @@ public: return nSamples; } + // Read up to nSamples elements into buffer. Returns the actual count read + // (may be less than requested if the buffer does not have enough data). unsigned int getData(T* buffer, unsigned int nSamples) { unsigned int data = dataSize(); @@ -73,6 +97,7 @@ public: return nSamples; } + // Like getData() but does not advance the read pointer (non-destructive). unsigned int peek(T* buffer, unsigned int nSamples) { unsigned int data = dataSize(); @@ -112,7 +137,7 @@ public: bool hasSpace(unsigned int length) const { - return freeSpace() > length; + return freeSpace() >= length; } bool hasData() const @@ -126,10 +151,10 @@ public: } private: - unsigned int m_length; - T* m_buffer; - volatile unsigned int m_iPtr; - volatile unsigned int m_oPtr; + unsigned int m_length; // Allocated slot count (capacity + 1) + T* m_buffer; + std::atomic m_iPtr; // Write index; advanced only by the producer + std::atomic m_oPtr; // Read index; advanced only by the consumer unsigned int dataSize() const { diff --git a/Common/SerialDataController.cpp b/Common/SerialDataController.cpp index 5a74d97..0bb4896 100644 --- a/Common/SerialDataController.cpp +++ b/Common/SerialDataController.cpp @@ -19,12 +19,8 @@ #include "SerialDataController.h" +#if !defined(_WIN32) #include - -#if defined(__WINDOWS__) -#include -#include -#else #include #include #include @@ -34,253 +30,15 @@ #endif -#if defined(__WINDOWS__) - -const unsigned int BUFFER_LENGTH = 1000U; +#if !defined(_WIN32) -CSerialDataController::CSerialDataController(const wxString& device, SERIAL_SPEED speed, bool assertRTS) : -m_device(device), -m_speed(speed), -m_assertRTS(assertRTS), -m_handle(INVALID_HANDLE_VALUE), -m_readOverlapped(), -m_writeOverlapped(), -m_readBuffer(NULL), -m_readLength(0U), -m_readPending(false) -{ - wxASSERT(!device.IsEmpty()); - - m_readBuffer = new unsigned char[BUFFER_LENGTH]; -} - -CSerialDataController::~CSerialDataController() -{ - delete[] m_readBuffer; -} - -bool CSerialDataController::open() -{ - wxASSERT(m_handle == INVALID_HANDLE_VALUE); - - DWORD errCode; - - wxString baseName = m_device.Mid(4U); // Convert "\\.\COM10" to "COM10" - - m_handle = ::CreateFile(m_device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - if (m_handle == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Cannot open device - %s, err=%04lx"), m_device.c_str(), ::GetLastError()); - return false; - } - - DCB dcb; - if (::GetCommState(m_handle, &dcb) == 0) { - wxLogError(wxT("Cannot get the attributes for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); - ::ClearCommError(m_handle, &errCode, NULL); - ::CloseHandle(m_handle); - return false; - } - - dcb.BaudRate = DWORD(m_speed); - dcb.ByteSize = 8; - dcb.Parity = NOPARITY; - dcb.fParity = FALSE; - dcb.StopBits = ONESTOPBIT; - dcb.fInX = FALSE; - dcb.fOutX = FALSE; - dcb.fOutxCtsFlow = FALSE; - dcb.fOutxDsrFlow = FALSE; - dcb.fDtrControl = DTR_CONTROL_DISABLE; - dcb.fRtsControl = RTS_CONTROL_DISABLE; - - if (::SetCommState(m_handle, &dcb) == 0) { - wxLogError(wxT("Cannot set the attributes for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); - ::ClearCommError(m_handle, &errCode, NULL); - ::CloseHandle(m_handle); - return false; - } - - COMMTIMEOUTS timeouts; - if (!::GetCommTimeouts(m_handle, &timeouts)) { - wxLogError(wxT("Cannot get the timeouts for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); - ::ClearCommError(m_handle, &errCode, NULL); - ::CloseHandle(m_handle); - return false; - } - - timeouts.ReadIntervalTimeout = MAXDWORD; - timeouts.ReadTotalTimeoutMultiplier = 0UL; - timeouts.ReadTotalTimeoutConstant = 0UL; - - if (!::SetCommTimeouts(m_handle, &timeouts)) { - wxLogError(wxT("Cannot set the timeouts for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); - ::ClearCommError(m_handle, &errCode, NULL); - ::CloseHandle(m_handle); - return false; - } - - if (::EscapeCommFunction(m_handle, CLRDTR) == 0) { - wxLogError(wxT("Cannot clear DTR for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); - ::ClearCommError(m_handle, &errCode, NULL); - ::CloseHandle(m_handle); - return false; - } - - if (::EscapeCommFunction(m_handle, m_assertRTS ? SETRTS : CLRRTS) == 0) { - wxLogError(wxT("Cannot set/clear RTS for %s, err=%04lx"), m_device.c_str(), ::GetLastError()); - ::ClearCommError(m_handle, &errCode, NULL); - ::CloseHandle(m_handle); - return false; - } - - ::ClearCommError(m_handle, &errCode, NULL); - - ::memset(&m_readOverlapped, 0x00U, sizeof(OVERLAPPED)); - ::memset(&m_writeOverlapped, 0x00U, sizeof(OVERLAPPED)); - - m_readOverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); - m_writeOverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); - - m_readLength = 0U; - m_readPending = false; - ::memset(m_readBuffer, 0x00U, BUFFER_LENGTH); - - return true; -} - -int CSerialDataController::read(unsigned char* buffer, unsigned int length, unsigned int timeout) -{ - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - wxASSERT(buffer != NULL); - - unsigned int ptr = 0U; - - while (ptr < length) { - int ret = readNonblock(buffer + ptr, length - ptr, timeout); - if (ret < 0) { - return ret; - } else if (ret == 0) { - if (ptr == 0U) - return 0; - } else { - ptr += ret; - } - } - - return int(length); -} - -int CSerialDataController::readNonblock(unsigned char* buffer, unsigned int length, unsigned int timeout) -{ - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - wxASSERT(buffer != NULL); - - if (length > BUFFER_LENGTH) - length = BUFFER_LENGTH; - - if (m_readPending && length != m_readLength) { - ::CancelIo(m_handle); - m_readPending = false; - } - - m_readLength = length; - - if (length == 0U) - return 0; - - if (!m_readPending) { - DWORD bytes = 0UL; - BOOL res = ::ReadFile(m_handle, m_readBuffer, m_readLength, &bytes, &m_readOverlapped); - if (res) { - ::memcpy(buffer, m_readBuffer, bytes); - return int(bytes); - } - - DWORD error = ::GetLastError(); - if (error != ERROR_IO_PENDING) { - wxLogError(wxT("Error from ReadFile: %04lx"), error); - return -1; - } - - m_readPending = true; - } - - DWORD bytes = 0UL; - DWORD millis = timeout; - BOOL res = ::GetOverlappedResultEx(m_handle, &m_readOverlapped, &bytes, millis, FALSE); - if (!res) { - DWORD error = ::GetLastError(); - if (timeout == 0U && error == ERROR_IO_INCOMPLETE) { - return 0; - } else if (timeout > 0U && error == WAIT_TIMEOUT) { - return 0; - } else if (timeout > 0U && error == WAIT_IO_COMPLETION) { - return 0; - } else { - wxLogError(wxT("Error from GetOverlappedResultEx (ReadFile): %04lx"), error); - return -1; - } - } - - ::memcpy(buffer, m_readBuffer, bytes); - m_readPending = false; - - return int(bytes); -} - -int CSerialDataController::write(const unsigned char* buffer, unsigned int length) -{ - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - wxASSERT(buffer != NULL); - - if (length == 0U) - return 0; - - unsigned int ptr = 0U; - - while (ptr < length) { - DWORD bytes = 0UL; - BOOL res = ::WriteFile(m_handle, buffer + ptr, length - ptr, &bytes, &m_writeOverlapped); - if (!res) { - DWORD error = ::GetLastError(); - if (error != ERROR_IO_PENDING) { - wxLogError(wxT("Error from WriteFile: %04lx"), error); - return -1; - } - - res = ::GetOverlappedResult(m_handle, &m_writeOverlapped, &bytes, TRUE); - if (!res) { - wxLogError(wxT("Error from GetOverlappedResult (WriteFile): %04lx"), ::GetLastError()); - return -1; - } - } - - ptr += bytes; - } - - return int(length); -} - -void CSerialDataController::close() -{ - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - - ::CloseHandle(m_handle); - m_handle = INVALID_HANDLE_VALUE; - - ::CloseHandle(m_readOverlapped.hEvent); - ::CloseHandle(m_writeOverlapped.hEvent); -} - -#else - -CSerialDataController::CSerialDataController(const wxString& device, SERIAL_SPEED speed, bool assertRTS) : +CSerialDataController::CSerialDataController(const std::string& device, SERIAL_SPEED speed, bool assertRTS) : m_device(device), m_speed(speed), m_assertRTS(assertRTS), m_fd(-1) { - wxASSERT(!device.IsEmpty()); + assert(!device.empty()); } CSerialDataController::~CSerialDataController() @@ -289,23 +47,23 @@ CSerialDataController::~CSerialDataController() bool CSerialDataController::open() { - wxASSERT(m_fd == -1); + assert(m_fd == -1); - m_fd = ::open(m_device.mb_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); + m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); if (m_fd < 0) { - wxLogError(wxT("Cannot open device - %s"), m_device.c_str()); + ::fprintf(stderr, "Cannot open device - %s\n", m_device.c_str()); return false; } if (::isatty(m_fd) == 0) { - wxLogError(wxT("%s is not a TTY device"), m_device.c_str()); + ::fprintf(stderr, "%s is not a TTY device\n", m_device.c_str()); ::close(m_fd); return false; } termios termios; if (::tcgetattr(m_fd, &termios) < 0) { - wxLogError(wxT("Cannot get the attributes for %s"), m_device.c_str()); + ::fprintf(stderr, "Cannot get the attributes for %s\n", m_device.c_str()); ::close(m_fd); return false; } @@ -352,13 +110,13 @@ bool CSerialDataController::open() ::cfsetispeed(&termios, B230400); break; default: - wxLogError(wxT("Unsupported serial port speed - %d"), int(m_speed)); + ::fprintf(stderr, "Unsupported serial port speed - %d\n", int(m_speed)); ::close(m_fd); return false; } if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { - wxLogError(wxT("Cannot set the attributes for %s"), m_device.c_str()); + ::fprintf(stderr, "Cannot set the attributes for %s\n", m_device.c_str()); ::close(m_fd); return false; } @@ -366,15 +124,15 @@ bool CSerialDataController::open() if (m_assertRTS) { unsigned int y; if (::ioctl(m_fd, TIOCMGET, &y) < 0) { - wxLogError(wxT("Cannot get the control attributes for %s"), m_device.c_str()); + ::fprintf(stderr, "Cannot get the control attributes for %s\n", m_device.c_str()); ::close(m_fd); return false; } y |= TIOCM_RTS; - + if (::ioctl(m_fd, TIOCMSET, &y) < 0) { - wxLogError(wxT("Cannot set the control attributes for %s"), m_device.c_str()); + ::fprintf(stderr, "Cannot set the control attributes for %s\n", m_device.c_str()); ::close(m_fd); return false; } @@ -385,8 +143,8 @@ bool CSerialDataController::open() int CSerialDataController::read(unsigned char* buffer, unsigned int length, unsigned int timeout) { - wxASSERT(buffer != NULL); - wxASSERT(m_fd != -1); + assert(buffer != nullptr); + assert(m_fd != -1); if (length == 0U) return 0; @@ -404,15 +162,15 @@ int CSerialDataController::read(unsigned char* buffer, unsigned int length, unsi tv.tv_sec = timeout / 1000U; tv.tv_usec = (timeout % 1000U) * 1000U; - n = ::select(m_fd + 1, &fds, NULL, NULL, &tv); + n = ::select(m_fd + 1, &fds, nullptr, nullptr, &tv); if (n == 0) return 0; } else { - n = ::select(m_fd + 1, &fds, NULL, NULL, NULL); + n = ::select(m_fd + 1, &fds, nullptr, nullptr, nullptr); } if (n < 0) { - wxLogError(wxT("Error from select(), errno=%d"), errno); + ::fprintf(stderr, "Error from select(), errno=%d\n", errno); return -1; } @@ -420,7 +178,7 @@ int CSerialDataController::read(unsigned char* buffer, unsigned int length, unsi ssize_t len = ::read(m_fd, buffer + offset, length - offset); if (len < 0) { if (errno != EAGAIN) { - wxLogError(wxT("Error from read(), errno=%d"), errno); + ::fprintf(stderr, "Error from read(), errno=%d\n", errno); return -1; } } @@ -435,8 +193,8 @@ int CSerialDataController::read(unsigned char* buffer, unsigned int length, unsi int CSerialDataController::write(const unsigned char* buffer, unsigned int length) { - wxASSERT(buffer != NULL); - wxASSERT(m_fd != -1); + assert(buffer != nullptr); + assert(m_fd != -1); if (length == 0U) return 0; @@ -447,7 +205,7 @@ int CSerialDataController::write(const unsigned char* buffer, unsigned int lengt ssize_t n = ::write(m_fd, buffer + ptr, length - ptr); if (n < 0) { if (errno != EAGAIN) { - wxLogError(wxT("Error returned from write(), errno=%d"), errno); + ::fprintf(stderr, "Error returned from write(), errno=%d\n", errno); return -1; } } @@ -461,10 +219,184 @@ int CSerialDataController::write(const unsigned char* buffer, unsigned int lengt void CSerialDataController::close() { - wxASSERT(m_fd != -1); + assert(m_fd != -1); ::close(m_fd); m_fd = -1; } -#endif +#else // _WIN32 + +CSerialDataController::CSerialDataController(const std::string& device, SERIAL_SPEED speed, bool assertRTS) : +m_device(device), +m_speed(speed), +m_assertRTS(assertRTS), +m_handle(INVALID_HANDLE_VALUE) +{ + assert(!device.empty()); +} + +CSerialDataController::~CSerialDataController() +{ +} + +bool CSerialDataController::open() +{ + assert(m_handle == INVALID_HANDLE_VALUE); + + // On Windows, ports above COM9 require the \\.\COMn prefix. + std::string path = "\\\\.\\" + m_device; + + m_handle = ::CreateFileA(path.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (m_handle == INVALID_HANDLE_VALUE) { + ::fprintf(stderr, "Cannot open device - %s\n", m_device.c_str()); + return false; + } + + DCB dcb; + ::memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(m_handle, &dcb)) { + ::fprintf(stderr, "Cannot get the attributes for %s\n", m_device.c_str()); + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; + return false; + } + + dcb.BaudRate = static_cast(m_speed); + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.StopBits = ONESTOPBIT; + dcb.fBinary = TRUE; + dcb.fParity = FALSE; + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + dcb.fOutX = FALSE; + dcb.fInX = FALSE; + + if (!::SetCommState(m_handle, &dcb)) { + ::fprintf(stderr, "Cannot set the attributes for %s\n", m_device.c_str()); + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; + return false; + } + + // Use infinite inter-character timeout; per-read timeout set in read(). + COMMTIMEOUTS timeouts; + ::memset(&timeouts, 0, sizeof(COMMTIMEOUTS)); + timeouts.ReadIntervalTimeout = 0; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + if (!::SetCommTimeouts(m_handle, &timeouts)) { + ::fprintf(stderr, "Cannot set the timeouts for %s\n", m_device.c_str()); + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; + return false; + } + + if (m_assertRTS) { + if (!::EscapeCommFunction(m_handle, SETRTS)) { + ::fprintf(stderr, "Cannot set RTS for %s\n", m_device.c_str()); + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; + return false; + } + } + + return true; +} + +int CSerialDataController::read(unsigned char* buffer, unsigned int length, unsigned int timeout) +{ + assert(buffer != nullptr); + assert(m_handle != INVALID_HANDLE_VALUE); + + if (length == 0U) + return 0; + + unsigned int offset = 0U; + + // Apply the caller's timeout to the first byte only. + COMMTIMEOUTS timeouts; + ::memset(&timeouts, 0, sizeof(COMMTIMEOUTS)); + timeouts.ReadIntervalTimeout = 0; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = (offset == 0U) ? static_cast(timeout) : MAXDWORD; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + ::SetCommTimeouts(m_handle, &timeouts); + + while (offset < length) { + if (offset == 1U) { + // After the first byte arrives, block indefinitely for the rest. + timeouts.ReadTotalTimeoutConstant = MAXDWORD; + ::SetCommTimeouts(m_handle, &timeouts); + } + + DWORD bytesRead = 0U; + BOOL ok = ::ReadFile(m_handle, + buffer + offset, + length - offset, + &bytesRead, + nullptr); + if (!ok) { + ::fprintf(stderr, "Error from ReadFile(), error=%lu\n", ::GetLastError()); + return -1; + } + + if (bytesRead == 0U && offset == 0U) + return 0; // timeout on first byte + + offset += bytesRead; + } + + return static_cast(length); +} + +int CSerialDataController::write(const unsigned char* buffer, unsigned int length) +{ + assert(buffer != nullptr); + assert(m_handle != INVALID_HANDLE_VALUE); + + if (length == 0U) + return 0; + + unsigned int ptr = 0U; + + while (ptr < length) { + DWORD bytesWritten = 0U; + BOOL ok = ::WriteFile(m_handle, + buffer + ptr, + length - ptr, + &bytesWritten, + nullptr); + if (!ok) { + ::fprintf(stderr, "Error returned from WriteFile(), error=%lu\n", ::GetLastError()); + return -1; + } + + ptr += bytesWritten; + } + + return static_cast(length); +} + +void CSerialDataController::close() +{ + assert(m_handle != INVALID_HANDLE_VALUE); + + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; +} + +#endif // _WIN32 diff --git a/Common/SerialDataController.h b/Common/SerialDataController.h index 525054f..03af4d8 100644 --- a/Common/SerialDataController.h +++ b/Common/SerialDataController.h @@ -20,9 +20,9 @@ #ifndef SerialDataController_H #define SerialDataController_H -#include +#include "StdCompat.h" -#if defined(__WINDOWS__) +#if defined(_WIN32) #include #endif @@ -38,36 +38,38 @@ enum SERIAL_SPEED { SERIAL_230400 = 230400 }; +/* + * Binary data I/O over a serial port. + * + * Used to communicate with modem hardware (DVAP, DV-RPTR, MMDVM, etc.) that + * is attached via a serial or USB-serial interface. Provides a simple + * read/write interface with optional read timeout (milliseconds). + * + * If assertRTS is true, the RTS line is asserted after open() — some modems + * use this as a hardware flow-control or power signal. + */ class CSerialDataController { public: - CSerialDataController(const wxString& device, SERIAL_SPEED speed, bool assertRTS = false); + CSerialDataController(const std::string& device, SERIAL_SPEED speed, bool assertRTS = false); ~CSerialDataController(); bool open(); + // Returns bytes read, 0 on timeout, or -1 on error. timeout=0 is non-blocking. int read(unsigned char* buffer, unsigned int length, unsigned int timeout = 0U); int write(const unsigned char* buffer, unsigned int length); void close(); private: - wxString m_device; + std::string m_device; SERIAL_SPEED m_speed; bool m_assertRTS; -#if defined(__WINDOWS__) +#if defined(_WIN32) HANDLE m_handle; - OVERLAPPED m_readOverlapped; - OVERLAPPED m_writeOverlapped; - unsigned char* m_readBuffer; - unsigned int m_readLength; - bool m_readPending; #else int m_fd; #endif - -#if defined(__WINDOWS__) - int readNonblock(unsigned char* buffer, unsigned int length, unsigned int timeout); -#endif }; #endif diff --git a/Common/SerialLineController.cpp b/Common/SerialLineController.cpp index a42324a..f234110 100644 --- a/Common/SerialLineController.cpp +++ b/Common/SerialLineController.cpp @@ -19,12 +19,8 @@ #include "SerialLineController.h" +#if !defined(_WIN32) #include - -#if defined(__WINDOWS__) -#include -#include -#else #include #include #include @@ -34,17 +30,17 @@ #endif -#if defined(__WINDOWS__) +#if !defined(_WIN32) -CSerialLineController::CSerialLineController(const wxString& device, unsigned int config) : +CSerialLineController::CSerialLineController(const std::string& device, unsigned int config) : m_device(device), m_config(config), m_rts(false), m_dtr(false), -m_handle(INVALID_HANDLE_VALUE) +m_fd(-1) { - wxASSERT(!device.IsEmpty()); - wxASSERT(config == 1U || config == 2U || config == 3U); + assert(!device.empty()); + assert(config == 1U || config == 2U || config == 3U); } CSerialLineController::~CSerialLineController() @@ -53,111 +49,111 @@ CSerialLineController::~CSerialLineController() bool CSerialLineController::open() { - wxASSERT(m_handle == INVALID_HANDLE_VALUE); + assert(m_fd == -1); - DWORD errCode; + m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); + if (m_fd < 0) { + ::fprintf(stderr, "Cannot open device - %s\n", m_device.c_str()); + return false; + } - m_handle = ::CreateFile(m_device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - if (m_handle == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Cannot open device - %s"), m_device.c_str()); + if (::isatty(m_fd) == 0) { + ::fprintf(stderr, "%s is not a TTY device\n", m_device.c_str()); + ::close(m_fd); return false; } - DCB dcb; - if (::GetCommState(m_handle, &dcb) == 0) { - wxLogError(wxT("Cannot get the attributes for %s"), m_device.c_str()); - ::ClearCommError(m_handle, &errCode, NULL); - ::CloseHandle(m_handle); + termios termios; + if (::tcgetattr(m_fd, &termios) < 0) { + ::fprintf(stderr, "Cannot get the attributes for %s\n", m_device.c_str()); + ::close(m_fd); return false; } - dcb.fOutxCtsFlow = FALSE; - dcb.fOutxDsrFlow = FALSE; - dcb.fDtrControl = DTR_CONTROL_DISABLE; - dcb.fRtsControl = RTS_CONTROL_DISABLE; + // FIXME XXX unfinished + termios.c_cflag |= (CLOCAL | CREAD); + termios.c_cflag &= ~CRTSCTS; + termios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + termios.c_oflag &= ~OPOST; + termios.c_cc[VMIN] = 0; + termios.c_cc[VTIME] = 10; - if (::SetCommState(m_handle, &dcb) == 0) { - wxLogError(wxT("Cannot set the attributes for %s"), m_device.c_str()); - ::ClearCommError(m_handle, &errCode, NULL); - ::CloseHandle(m_handle); + if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { + ::fprintf(stderr, "Cannot set the attributes for %s\n", m_device.c_str()); + ::close(m_fd); return false; } - if (::EscapeCommFunction(m_handle, CLRDTR) == 0) { - wxLogError(wxT("Cannot clear DTR for %s"), m_device.c_str()); - ::ClearCommError(m_handle, &errCode, NULL); - ::CloseHandle(m_handle); + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) { + ::fprintf(stderr, "Cannot get the modem status bits for %s\n", m_device.c_str()); + ::close(m_fd); return false; } - if (::EscapeCommFunction(m_handle, CLRRTS) == 0) { - wxLogError(wxT("Cannot clear RTS for %s"), m_device.c_str()); - ::ClearCommError(m_handle, &errCode, NULL); - ::CloseHandle(m_handle); + y &= ~TIOCM_DTR; + y &= ~TIOCM_RTS; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) { + ::fprintf(stderr, "Cannot set the modem status bits for %s\n", m_device.c_str()); + ::close(m_fd); return false; } - ::ClearCommError(m_handle, &errCode, NULL); - return true; } bool CSerialLineController::getCD() const { - wxASSERT(m_handle != INVALID_HANDLE_VALUE); + assert(m_fd != -1); - DWORD status; - DWORD errCode; - if (::GetCommModemStatus(m_handle, &status) == 0) { - ::ClearCommError(m_handle, &errCode, NULL); + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) return false; - } - return (status & MS_RLSD_ON) == MS_RLSD_ON; + return (y & TIOCM_CD) == TIOCM_CD; } bool CSerialLineController::getCTS() const { - wxASSERT(m_handle != INVALID_HANDLE_VALUE); + assert(m_fd != -1); - DWORD status; - DWORD errCode; - if (::GetCommModemStatus(m_handle, &status) == 0) { - ::ClearCommError(m_handle, &errCode, NULL); + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) return false; - } - return (status & MS_CTS_ON) == MS_CTS_ON; + return (y & TIOCM_CTS) == TIOCM_CTS; } bool CSerialLineController::getDSR() const { - wxASSERT(m_handle != INVALID_HANDLE_VALUE); + assert(m_fd != -1); - DWORD status; - DWORD errCode; - if (::GetCommModemStatus(m_handle, &status) == 0) { - ::ClearCommError(m_handle, &errCode, NULL); + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) return false; - } - return (status & MS_DSR_ON) == MS_DSR_ON; + return (y & TIOCM_DSR) == TIOCM_DSR; } bool CSerialLineController::setRTS(bool set) { - wxASSERT(m_handle != INVALID_HANDLE_VALUE); + assert(m_fd != -1); if (set == m_rts) return true; - DWORD rts = (set) ? SETRTS : CLRRTS; - DWORD errCode; + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return false; + + if (set) + y |= TIOCM_RTS; + else + y &= ~TIOCM_RTS; - if (::EscapeCommFunction(m_handle, rts) == 0) { - ::ClearCommError(m_handle, &errCode, NULL); + if (::ioctl(m_fd, TIOCMSET, &y) < 0) return false; - } m_rts = set; @@ -166,18 +162,22 @@ bool CSerialLineController::setRTS(bool set) bool CSerialLineController::setDTR(bool set) { - wxASSERT(m_handle != INVALID_HANDLE_VALUE); + assert(m_fd != -1); if (set == m_dtr) return true; - DWORD dtr = (set) ? SETDTR : CLRDTR; - DWORD errCode; + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return false; - if (::EscapeCommFunction(m_handle, dtr) == 0) { - ::ClearCommError(m_handle, &errCode, NULL); + if (set) + y |= TIOCM_DTR; + else + y &= ~TIOCM_DTR; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) return false; - } m_dtr = set; @@ -186,116 +186,106 @@ bool CSerialLineController::setDTR(bool set) void CSerialLineController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) { - wxASSERT(m_handle != INVALID_HANDLE_VALUE); + assert(m_fd != -1); inp1 = inp2 = inp3 = inp4 = inp5 = false; - DWORD status; - DWORD errCode; - if (::GetCommModemStatus(m_handle, &status) == 0) { - ::ClearCommError(m_handle, &errCode, NULL); + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) return; - } switch (m_config) { case 1U: - inp1 = (status & MS_DSR_ON) == MS_DSR_ON; - inp2 = (status & MS_CTS_ON) == MS_CTS_ON; + inp1 = (y & TIOCM_DSR) == TIOCM_DSR; + inp2 = (y & TIOCM_CTS) == TIOCM_CTS; break; case 2U: - inp1 = (status & MS_RLSD_ON) == MS_RLSD_ON; - inp2 = (status & MS_RLSD_ON) == MS_RLSD_ON; + inp1 = (y & TIOCM_CD) == TIOCM_CD; + inp2 = (y & TIOCM_CD) == TIOCM_CD; break; case 3U: - inp1 = (status & MS_RLSD_ON) == MS_RLSD_ON; + inp1 = (y & TIOCM_CD) == TIOCM_CD; break; default: - wxLogError(wxT("Unknown serial config - %u"), m_config); + ::fprintf(stderr, "Unknown serial config - %u\n", m_config); break; } } void CSerialLineController::setDigitalOutputs(bool outp1, bool, bool outp3, bool, bool, bool, bool, bool) { - wxASSERT(m_handle != INVALID_HANDLE_VALUE); + assert(m_fd != -1); - switch (m_config) { - case 1U: - if (outp1 != m_dtr) { - DWORD dtr = (outp1) ? SETDTR : CLRDTR; - DWORD errCode; - - if (::EscapeCommFunction(m_handle, dtr) == 0) { - ::ClearCommError(m_handle, &errCode, NULL); - return; - } - - m_dtr = outp1; - } - if (outp3 != m_rts) { - DWORD rts = (outp3) ? SETRTS : CLRRTS; - DWORD errCode; - - if (::EscapeCommFunction(m_handle, rts) == 0) { - ::ClearCommError(m_handle, &errCode, NULL); - return; - } - - m_rts = outp3; - } - break; + if (m_config == 1U) { + if (outp1 == m_dtr && outp3 == m_rts) + return; - case 2U: - case 3U: - if (outp1 != m_rts) { - DWORD rts = (outp1) ? SETRTS : CLRRTS; - DWORD errCode; - - if (::EscapeCommFunction(m_handle, rts) == 0) { - ::ClearCommError(m_handle, &errCode, NULL); - return; - } - - m_rts = outp1; - } - if (outp3 != m_dtr) { - DWORD dtr = (outp3) ? SETDTR : CLRDTR; - DWORD errCode; - - if (::EscapeCommFunction(m_handle, dtr) == 0) { - ::ClearCommError(m_handle, &errCode, NULL); - return; - } - - m_dtr = outp3; - } - break; + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return; - default: - wxLogError(wxT("Unknown serial config - %u"), m_config); - break; + if (outp1) + y |= TIOCM_DTR; + else + y &= ~TIOCM_DTR; + + if (outp3) + y |= TIOCM_RTS; + else + y &= ~TIOCM_RTS; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) + return; + + m_dtr = outp1; + m_rts = outp3; + } else if (m_config == 2U || m_config == 3U) { + if (outp1 == m_rts && outp3 == m_dtr) + return; + + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) + return; + + if (outp1) + y |= TIOCM_RTS; + else + y &= ~TIOCM_RTS; + + if (outp3) + y |= TIOCM_DTR; + else + y &= ~TIOCM_DTR; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) + return; + + m_rts = outp1; + m_dtr = outp3; + } else { + ::fprintf(stderr, "Unknown serial config - %u\n", m_config); } } void CSerialLineController::close() { - wxASSERT(m_handle != INVALID_HANDLE_VALUE); + assert(m_fd != -1); - ::CloseHandle(m_handle); - m_handle = INVALID_HANDLE_VALUE; + ::close(m_fd); + m_fd = -1; } -#else +#else // _WIN32 -CSerialLineController::CSerialLineController(const wxString& device, unsigned int config) : +CSerialLineController::CSerialLineController(const std::string& device, unsigned int config) : m_device(device), m_config(config), m_rts(false), m_dtr(false), -m_fd(-1) +m_handle(INVALID_HANDLE_VALUE) { - wxASSERT(!device.IsEmpty()); - wxASSERT(config == 1U || config == 2U || config == 3U); + assert(!device.empty()); + assert(config == 1U || config == 2U || config == 3U); } CSerialLineController::~CSerialLineController() @@ -304,110 +294,74 @@ CSerialLineController::~CSerialLineController() bool CSerialLineController::open() { - wxASSERT(m_fd == -1); - - m_fd = ::open(m_device.mb_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); - if (m_fd < 0) { - wxLogError(wxT("Cannot open device - %s"), m_device.c_str()); - return false; - } - - if (::isatty(m_fd) == 0) { - wxLogError(wxT("%s is not a TTY device"), m_device.c_str()); - ::close(m_fd); - return false; - } - - termios termios; - if (::tcgetattr(m_fd, &termios) < 0) { - wxLogError(wxT("Cannot get the attributes for %s"), m_device.c_str()); - ::close(m_fd); - return false; - } - - // FIXME XXX unfinished - termios.c_cflag |= (CLOCAL | CREAD); - termios.c_cflag &= ~CRTSCTS; - termios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); - termios.c_oflag &= ~OPOST; - termios.c_cc[VMIN] = 0; - termios.c_cc[VTIME] = 10; - - if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { - wxLogError(wxT("Cannot set the attributes for %s"), m_device.c_str()); - ::close(m_fd); - return false; - } - - unsigned int y; - if (::ioctl(m_fd, TIOCMGET, &y) < 0) { - wxLogError(wxT("Cannot get the modem status bits for %s"), m_device.c_str()); - ::close(m_fd); + assert(m_handle == INVALID_HANDLE_VALUE); + + // On Windows, ports above COM9 require the \\.\COMn prefix. + std::string path = "\\\\.\\" + m_device; + + m_handle = ::CreateFileA(path.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (m_handle == INVALID_HANDLE_VALUE) { + ::fprintf(stderr, "Cannot open device - %s\n", m_device.c_str()); return false; } - y &= ~TIOCM_DTR; - y &= ~TIOCM_RTS; + // Deassert both DTR and RTS on open, matching the POSIX behaviour. + ::EscapeCommFunction(m_handle, CLRDTR); + ::EscapeCommFunction(m_handle, CLRRTS); - if (::ioctl(m_fd, TIOCMSET, &y) < 0) { - wxLogError(wxT("Cannot set the modem status bits for %s"), m_device.c_str()); - ::close(m_fd); - return false; - } + m_rts = false; + m_dtr = false; return true; } bool CSerialLineController::getCD() const { - wxASSERT(m_fd != -1); + assert(m_handle != INVALID_HANDLE_VALUE); - unsigned int y; - if (::ioctl(m_fd, TIOCMGET, &y) < 0) + DWORD status = 0U; + if (!::GetCommModemStatus(m_handle, &status)) return false; - return (y & TIOCM_CD) == TIOCM_CD; + return (status & MS_RLSD_ON) != 0U; } bool CSerialLineController::getCTS() const { - wxASSERT(m_fd != -1); + assert(m_handle != INVALID_HANDLE_VALUE); - unsigned int y; - if (::ioctl(m_fd, TIOCMGET, &y) < 0) + DWORD status = 0U; + if (!::GetCommModemStatus(m_handle, &status)) return false; - return (y & TIOCM_CTS) == TIOCM_CTS; + return (status & MS_CTS_ON) != 0U; } bool CSerialLineController::getDSR() const { - wxASSERT(m_fd != -1); + assert(m_handle != INVALID_HANDLE_VALUE); - unsigned int y; - if (::ioctl(m_fd, TIOCMGET, &y) < 0) + DWORD status = 0U; + if (!::GetCommModemStatus(m_handle, &status)) return false; - return (y & TIOCM_DSR) == TIOCM_DSR; + return (status & MS_DSR_ON) != 0U; } bool CSerialLineController::setRTS(bool set) { - wxASSERT(m_fd != -1); + assert(m_handle != INVALID_HANDLE_VALUE); if (set == m_rts) return true; - unsigned int y; - if (::ioctl(m_fd, TIOCMGET, &y) < 0) - return false; - - if (set) - y |= TIOCM_RTS; - else - y &= ~TIOCM_RTS; - - if (::ioctl(m_fd, TIOCMSET, &y) < 0) + if (!::EscapeCommFunction(m_handle, set ? SETRTS : CLRRTS)) return false; m_rts = set; @@ -417,21 +371,12 @@ bool CSerialLineController::setRTS(bool set) bool CSerialLineController::setDTR(bool set) { - wxASSERT(m_fd != -1); + assert(m_handle != INVALID_HANDLE_VALUE); if (set == m_dtr) return true; - unsigned int y; - if (::ioctl(m_fd, TIOCMGET, &y) < 0) - return false; - - if (set) - y |= TIOCM_DTR; - else - y &= ~TIOCM_DTR; - - if (::ioctl(m_fd, TIOCMSET, &y) < 0) + if (!::EscapeCommFunction(m_handle, set ? SETDTR : CLRDTR)) return false; m_dtr = set; @@ -441,56 +386,42 @@ bool CSerialLineController::setDTR(bool set) void CSerialLineController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) { - wxASSERT(m_fd != -1); + assert(m_handle != INVALID_HANDLE_VALUE); inp1 = inp2 = inp3 = inp4 = inp5 = false; - unsigned int y; - if (::ioctl(m_fd, TIOCMGET, &y) < 0) + DWORD status = 0U; + if (!::GetCommModemStatus(m_handle, &status)) return; switch (m_config) { case 1U: - inp1 = (y & TIOCM_DSR) == TIOCM_DSR; - inp2 = (y & TIOCM_CTS) == TIOCM_CTS; + inp1 = (status & MS_DSR_ON) != 0U; + inp2 = (status & MS_CTS_ON) != 0U; break; case 2U: - inp1 = (y & TIOCM_CD) == TIOCM_CD; - inp2 = (y & TIOCM_CD) == TIOCM_CD; + inp1 = (status & MS_RLSD_ON) != 0U; + inp2 = (status & MS_RLSD_ON) != 0U; break; case 3U: - inp1 = (y & TIOCM_CD) == TIOCM_CD; + inp1 = (status & MS_RLSD_ON) != 0U; break; default: - wxLogError(wxT("Unknown serial config - %u"), m_config); + ::fprintf(stderr, "Unknown serial config - %u\n", m_config); break; } } void CSerialLineController::setDigitalOutputs(bool outp1, bool, bool outp3, bool, bool, bool, bool, bool) { - wxASSERT(m_fd != -1); + assert(m_handle != INVALID_HANDLE_VALUE); if (m_config == 1U) { if (outp1 == m_dtr && outp3 == m_rts) return; - unsigned int y; - if (::ioctl(m_fd, TIOCMGET, &y) < 0) - return; - - if (outp1) - y |= TIOCM_DTR; - else - y &= ~TIOCM_DTR; - - if (outp3) - y |= TIOCM_RTS; - else - y &= ~TIOCM_RTS; - - if (::ioctl(m_fd, TIOCMSET, &y) < 0) - return; + ::EscapeCommFunction(m_handle, outp1 ? SETDTR : CLRDTR); + ::EscapeCommFunction(m_handle, outp3 ? SETRTS : CLRRTS); m_dtr = outp1; m_rts = outp3; @@ -498,36 +429,22 @@ void CSerialLineController::setDigitalOutputs(bool outp1, bool, bool outp3, bool if (outp1 == m_rts && outp3 == m_dtr) return; - unsigned int y; - if (::ioctl(m_fd, TIOCMGET, &y) < 0) - return; - - if (outp1) - y |= TIOCM_RTS; - else - y &= ~TIOCM_RTS; - - if (outp3) - y |= TIOCM_DTR; - else - y &= ~TIOCM_DTR; - - if (::ioctl(m_fd, TIOCMSET, &y) < 0) - return; + ::EscapeCommFunction(m_handle, outp1 ? SETRTS : CLRRTS); + ::EscapeCommFunction(m_handle, outp3 ? SETDTR : CLRDTR); m_rts = outp1; m_dtr = outp3; } else { - wxLogError(wxT("Unknown serial config - %u"), m_config); + ::fprintf(stderr, "Unknown serial config - %u\n", m_config); } } void CSerialLineController::close() { - wxASSERT(m_fd != -1); + assert(m_handle != INVALID_HANDLE_VALUE); - ::close(m_fd); - m_fd = -1; + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; } -#endif +#endif // _WIN32 diff --git a/Common/SerialLineController.h b/Common/SerialLineController.h index 827af7a..929c709 100644 --- a/Common/SerialLineController.h +++ b/Common/SerialLineController.h @@ -21,27 +21,39 @@ #define SerialLineController_H #include "HardwareController.h" +#include "StdCompat.h" -#include - -#if defined(__WINDOWS__) +#if defined(_WIN32) #include #endif +// Serial modem control lines used for PTT / COR signalling. enum SERIALPIN { - SERIAL_CD, - SERIAL_CTS, - SERIAL_DSR, - SERIAL_DTR, - SERIAL_RTS, - SERIAL_ECHOLINK + SERIAL_CD, // Carrier Detect — input, indicates received signal. + SERIAL_CTS, // Clear To Send — input. + SERIAL_DSR, // Data Set Ready — input. + SERIAL_DTR, // Data Terminal Ready — output, used for PTT on some hardware. + SERIAL_RTS, // Request To Send — output, most common PTT line. + SERIAL_ECHOLINK // EchoLink-compatible pin assignment variant. }; const unsigned int MAX_DEVICE_NAME = 255U; +/* + * Serial port line-level controller for PTT and COR/COS I/O. + * + * Implements IHardwareController using the modem-control lines of an RS-232 + * (or USB-serial) port. RTS and/or DTR are used as PTT outputs; CD, CTS, + * and DSR are used as COR/squelch inputs. The config parameter selects which + * pin assignment scheme is used (see the SERIALPIN enum and the controller + * config documentation). + * + * This controller does not transfer any data — it only toggles the control + * lines. For serial data transfer use CSerialDataController. + */ class CSerialLineController : public IHardwareController { public: - CSerialLineController(const wxString& device, unsigned int config = 1U); + CSerialLineController(const std::string& device, unsigned int config = 1U); virtual ~CSerialLineController(); virtual bool open(); @@ -59,11 +71,11 @@ public: virtual void close(); private: - wxString m_device; - unsigned int m_config; + std::string m_device; + unsigned int m_config; // Pin assignment scheme index. bool m_rts; bool m_dtr; -#if defined(__WINDOWS__) +#if defined(_WIN32) HANDLE m_handle; #else int m_fd; diff --git a/Common/SerialPortSelector.cpp b/Common/SerialPortSelector.cpp index bc7ad1c..956092c 100644 --- a/Common/SerialPortSelector.cpp +++ b/Common/SerialPortSelector.cpp @@ -18,7 +18,12 @@ #include "SerialPortSelector.h" +#if !defined(_WIN32) #include +#include +#include +#include +#endif #if defined(__APPLE__) && defined(__MACH__) #include @@ -29,19 +34,12 @@ #include #include #include -#elif defined(__WINDOWS__) -#include -#include -#else -#include #endif -wxArrayString CSerialPortSelector::getDevices() +std::vector CSerialPortSelector::getDevices() { - wxArrayString devices; - - devices.Alloc(10); + std::vector devices; #if defined(__APPLE__) && defined(__MACH__) mach_port_t masterPort; @@ -50,8 +48,8 @@ wxArrayString CSerialPortSelector::getDevices() return devices; CFMutableDictionaryRef match = ::IOServiceMatching(kIOSerialBSDServiceValue); - if (match == NULL) - wxLogWarning(wxT("IOServiceMatching() returned NULL")); + if (match == nullptr) + ::fprintf(stderr, "IOServiceMatching() returned nullptr\n"); else ::CFDictionarySetValue(match, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDRS232Type)); @@ -63,80 +61,60 @@ wxArrayString CSerialPortSelector::getDevices() io_object_t modem; while ((modem = ::IOIteratorNext(services))) { CFTypeRef filePath = ::IORegistryEntryCreateCFProperty(modem, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); - if (filePath != NULL) { + if (filePath != nullptr) { char port[MAXPATHLEN]; Boolean result = ::CFStringGetCString((const __CFString*)filePath, port, MAXPATHLEN, kCFStringEncodingASCII); ::CFRelease(filePath); if (result) - devices.Add(port); + devices.push_back(port); ::IOObjectRelease(modem); } } ::IOObjectRelease(services); -#elif defined(__WINDOWS__) - devices.Add(wxT("\\\\.\\COM1")); - devices.Add(wxT("\\\\.\\COM2")); - devices.Add(wxT("\\\\.\\COM3")); - devices.Add(wxT("\\\\.\\COM4")); - devices.Add(wxT("\\\\.\\COM5")); - devices.Add(wxT("\\\\.\\COM6")); - devices.Add(wxT("\\\\.\\COM7")); - devices.Add(wxT("\\\\.\\COM8")); - devices.Add(wxT("\\\\.\\COM9")); - devices.Add(wxT("\\\\.\\COM10")); - devices.Add(wxT("\\\\.\\COM11")); - devices.Add(wxT("\\\\.\\COM12")); - devices.Add(wxT("\\\\.\\COM13")); - devices.Add(wxT("\\\\.\\COM14")); - devices.Add(wxT("\\\\.\\COM15")); - devices.Add(wxT("\\\\.\\COM16")); - devices.Add(wxT("\\\\.\\COM17")); - devices.Add(wxT("\\\\.\\COM18")); - devices.Add(wxT("\\\\.\\COM19")); -#else - wxDir devDir; - bool ret = devDir.Open(wxT("/dev")); - - if (ret) { - wxString fileName; - ret = devDir.GetFirst(&fileName, wxT("ttyACM*"), wxDIR_FILES); - - while (ret) { - fileName.Prepend(wxT("/dev/")); - devices.Add(fileName); - - ret = devDir.GetNext(&fileName); - } - - ret = devDir.GetFirst(&fileName, wxT("ttyAMA*"), wxDIR_FILES); - - while (ret) { - fileName.Prepend(wxT("/dev/")); - devices.Add(fileName); - - ret = devDir.GetNext(&fileName); - } - - ret = devDir.GetFirst(&fileName, wxT("ttyS*"), wxDIR_FILES); - - while (ret) { - fileName.Prepend(wxT("/dev/")); - devices.Add(fileName); - - ret = devDir.GetNext(&fileName); +#elif defined(_WIN32) + // Windows: probe COM1 through COM32 with CreateFile. + for (int i = 1; i <= 32; ++i) { + char portName[16]; + ::sprintf_s(portName, sizeof(portName), "\\\\.\\COM%d", i); + + HANDLE h = ::CreateFileA(portName, + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (h != INVALID_HANDLE_VALUE) { + ::CloseHandle(h); + char name[8]; + ::sprintf_s(name, sizeof(name), "COM%d", i); + devices.push_back(name); } - - ret = devDir.GetFirst(&fileName, wxT("ttyUSB*"), wxDIR_FILES); - - while (ret) { - fileName.Prepend(wxT("/dev/")); - devices.Add(fileName); - - ret = devDir.GetNext(&fileName); + } +#else + // Linux: enumerate /dev for common serial port patterns + static const char* const patterns[] = { + "ttyACM", "ttyAMA", "ttyS", "ttyUSB", nullptr + }; + + DIR* devDir = ::opendir("/dev"); + if (devDir != nullptr) { + struct dirent* entry; + while ((entry = ::readdir(devDir)) != nullptr) { + if (entry->d_type != DT_CHR && entry->d_type != DT_LNK && entry->d_type != DT_UNKNOWN) + continue; + for (int i = 0; patterns[i] != nullptr; ++i) { + if (::strncmp(entry->d_name, patterns[i], ::strlen(patterns[i])) == 0) { + devices.push_back(std::string("/dev/") + entry->d_name); + break; + } + } } + ::closedir(devDir); + std::sort(devices.begin(), devices.end()); } #endif diff --git a/Common/SerialPortSelector.h b/Common/SerialPortSelector.h index 0d7c412..005d7bd 100644 --- a/Common/SerialPortSelector.h +++ b/Common/SerialPortSelector.h @@ -19,11 +19,11 @@ #ifndef SerialPortSelector_H #define SerialPortSelector_H -#include +#include "StdCompat.h" class CSerialPortSelector { public: - static wxArrayString getDevices(); + static std::vector getDevices(); private: }; diff --git a/Common/SlowDataDecoder.cpp b/Common/SlowDataDecoder.cpp index e75eb37..671c1ae 100644 --- a/Common/SlowDataDecoder.cpp +++ b/Common/SlowDataDecoder.cpp @@ -15,16 +15,19 @@ #include "SlowDataDecoder.h" #include "DStarDefines.h" +#include +#include + const unsigned int SLOW_DATA_BLOCK_SIZE = 6U; const unsigned int SLOW_DATA_FULL_BLOCK_SIZE = (SLOW_DATA_BLOCK_SIZE - 1U) * 10U; CSlowDataDecoder::CSlowDataDecoder() : -m_buffer(NULL), +m_buffer(nullptr), m_state(SDD_FIRST), -m_headerData(NULL), +m_headerData(nullptr), m_headerPtr(0U), -m_header(NULL) +m_header(nullptr) { m_buffer = new unsigned char[SLOW_DATA_BLOCK_SIZE]; m_headerData = new unsigned char[SLOW_DATA_FULL_BLOCK_SIZE]; @@ -40,7 +43,7 @@ CSlowDataDecoder::~CSlowDataDecoder() void CSlowDataDecoder::addData(const unsigned char* data) { - wxASSERT(data != NULL); + assert(data != nullptr); switch (m_state) { case SDD_FIRST: @@ -57,13 +60,13 @@ void CSlowDataDecoder::addData(const unsigned char* data) m_state = SDD_FIRST; processHeader(); break; - } + } } CHeaderData* CSlowDataDecoder::getHeaderData() { - if (m_header == NULL) - return NULL; + if (m_header == nullptr) + return nullptr; CHeaderData* temp = new CHeaderData(*m_header); @@ -88,13 +91,13 @@ void CSlowDataDecoder::reset() ::memset(m_headerData, 0x00, SLOW_DATA_FULL_BLOCK_SIZE * sizeof(unsigned char)); delete m_header; - m_header = NULL; + m_header = nullptr; } void CSlowDataDecoder::processHeader() { // Do we have a complete and valid header already? - if (m_header != NULL) + if (m_header != nullptr) return; for (unsigned int i = 1U; i <= (SLOW_DATA_BLOCK_SIZE - 1U); i++, m_headerPtr++) { diff --git a/Common/SlowDataDecoder.h b/Common/SlowDataDecoder.h index b628116..6969590 100644 --- a/Common/SlowDataDecoder.h +++ b/Common/SlowDataDecoder.h @@ -16,11 +16,28 @@ #include "HeaderData.h" -#include +/* + * Decodes D-Star slow data embedded in the 3-byte data field of each DV frame. + * + * D-Star slow data is multiplexed with voice at 1200 bps by stealing 3 bytes + * from every 12-byte DV frame. These 3 bytes are XOR-scrambled before + * transmission using a repeating 3-byte key (SCRAMBLER_BYTE1/2/3 from + * DStarDefines.h) to keep the bit pattern from looking like sync patterns. + * + * Two consecutive 3-byte slow-data fields are collected into a 6-byte block. + * The first byte of each block is a type/length tag; the remaining 5 bytes + * carry payload. Blocks can carry a re-transmitted copy of the radio header + * (type SLOW_DATA_TYPE_HEADER) or free-text (type SLOW_DATA_TYPE_TEXT). + * + * addData() is called once per DV frame with the 3-byte slow-data field (after + * stripping the AMBE bytes). It alternates between SDD_FIRST and SDD_SECOND + * states to accumulate complete 6-byte blocks. When a valid header checksum + * is found, the result is available via getHeaderData(). + */ enum SDD_STATE { - SDD_FIRST, - SDD_SECOND + SDD_FIRST, // Waiting for the first 3-byte half of a slow-data block. + SDD_SECOND // Waiting for the second half to complete the block. }; class CSlowDataDecoder { @@ -28,19 +45,23 @@ public: CSlowDataDecoder(); ~CSlowDataDecoder(); + // Feed one 3-byte slow-data field (raw, still scrambled). void addData(const unsigned char* data); + // Returns a newly allocated CHeaderData if a valid header was decoded, + // or nullptr if none has been found yet. Caller takes ownership. CHeaderData* getHeaderData(); + // Called on each data-sync frame to re-align the two-phase accumulator. void sync(); void reset(); private: - unsigned char* m_buffer; + unsigned char* m_buffer; // Accumulates the current 6-byte block. SDD_STATE m_state; - unsigned char* m_headerData; + unsigned char* m_headerData; // Circular buffer of decoded payload bytes. unsigned int m_headerPtr; - CHeaderData* m_header; + CHeaderData* m_header; // Decoded header, held until getHeaderData() is called. void processHeader(); bool processHeader(const unsigned char* bytes); diff --git a/Common/SlowDataEncoder.cpp b/Common/SlowDataEncoder.cpp index 74bbd4b..c8ea4ac 100644 --- a/Common/SlowDataEncoder.cpp +++ b/Common/SlowDataEncoder.cpp @@ -15,13 +15,16 @@ #include "SlowDataEncoder.h" #include "DStarDefines.h" +#include +#include + const unsigned int SLOW_DATA_BLOCK_SIZE = 6U; const unsigned int SLOW_DATA_FULL_BLOCK_SIZE = SLOW_DATA_BLOCK_SIZE * 10U; CSlowDataEncoder::CSlowDataEncoder() : -m_headerData(NULL), -m_textData(NULL), +m_headerData(nullptr), +m_textData(nullptr), m_headerPtr(0U), m_textPtr(0U) { @@ -40,7 +43,7 @@ CSlowDataEncoder::~CSlowDataEncoder() void CSlowDataEncoder::getHeaderData(unsigned char* data) { - wxASSERT(data != NULL); + assert(data != nullptr); data[0U] = m_headerData[m_headerPtr++] ^ SCRAMBLER_BYTE1; data[1U] = m_headerData[m_headerPtr++] ^ SCRAMBLER_BYTE2; @@ -52,7 +55,7 @@ void CSlowDataEncoder::getHeaderData(unsigned char* data) void CSlowDataEncoder::getTextData(unsigned char* data) { - wxASSERT(data != NULL); + assert(data != nullptr); data[0U] = m_textData[m_textPtr++] ^ SCRAMBLER_BYTE1; data[1U] = m_textData[m_textPtr++] ^ SCRAMBLER_BYTE2; @@ -85,56 +88,56 @@ void CSlowDataEncoder::setHeaderData(const CHeaderData& header) m_headerData[1U] = header.getFlag1(); m_headerData[2U] = header.getFlag2(); m_headerData[3U] = header.getFlag3(); - m_headerData[4U] = header.getRptCall2().GetChar(0); - m_headerData[5U] = header.getRptCall2().GetChar(1); + m_headerData[4U] = header.getRptCall2()[0]; + m_headerData[5U] = header.getRptCall2()[1]; m_headerData[6U] = SLOW_DATA_TYPE_HEADER | 5U; - m_headerData[7U] = header.getRptCall2().GetChar(2); - m_headerData[8U] = header.getRptCall2().GetChar(3); - m_headerData[9U] = header.getRptCall2().GetChar(4); - m_headerData[10U] = header.getRptCall2().GetChar(5); - m_headerData[11U] = header.getRptCall2().GetChar(6); + m_headerData[7U] = header.getRptCall2()[2]; + m_headerData[8U] = header.getRptCall2()[3]; + m_headerData[9U] = header.getRptCall2()[4]; + m_headerData[10U] = header.getRptCall2()[5]; + m_headerData[11U] = header.getRptCall2()[6]; m_headerData[12U] = SLOW_DATA_TYPE_HEADER | 5U; - m_headerData[13U] = header.getRptCall2().GetChar(7); - m_headerData[14U] = header.getRptCall1().GetChar(0); - m_headerData[15U] = header.getRptCall1().GetChar(1); - m_headerData[16U] = header.getRptCall1().GetChar(2); - m_headerData[17U] = header.getRptCall1().GetChar(3); + m_headerData[13U] = header.getRptCall2()[7]; + m_headerData[14U] = header.getRptCall1()[0]; + m_headerData[15U] = header.getRptCall1()[1]; + m_headerData[16U] = header.getRptCall1()[2]; + m_headerData[17U] = header.getRptCall1()[3]; m_headerData[18U] = SLOW_DATA_TYPE_HEADER | 5U; - m_headerData[19U] = header.getRptCall1().GetChar(4); - m_headerData[20U] = header.getRptCall1().GetChar(5); - m_headerData[21U] = header.getRptCall1().GetChar(6); - m_headerData[22U] = header.getRptCall1().GetChar(7); - m_headerData[23U] = header.getYourCall().GetChar(0); + m_headerData[19U] = header.getRptCall1()[4]; + m_headerData[20U] = header.getRptCall1()[5]; + m_headerData[21U] = header.getRptCall1()[6]; + m_headerData[22U] = header.getRptCall1()[7]; + m_headerData[23U] = header.getYourCall()[0]; m_headerData[24U] = SLOW_DATA_TYPE_HEADER | 5U; - m_headerData[25U] = header.getYourCall().GetChar(1); - m_headerData[26U] = header.getYourCall().GetChar(2); - m_headerData[27U] = header.getYourCall().GetChar(3); - m_headerData[28U] = header.getYourCall().GetChar(4); - m_headerData[29U] = header.getYourCall().GetChar(5); + m_headerData[25U] = header.getYourCall()[1]; + m_headerData[26U] = header.getYourCall()[2]; + m_headerData[27U] = header.getYourCall()[3]; + m_headerData[28U] = header.getYourCall()[4]; + m_headerData[29U] = header.getYourCall()[5]; m_headerData[30U] = SLOW_DATA_TYPE_HEADER | 5U; - m_headerData[31U] = header.getYourCall().GetChar(6); - m_headerData[32U] = header.getYourCall().GetChar(7); - m_headerData[33U] = header.getMyCall1().GetChar(0); - m_headerData[34U] = header.getMyCall1().GetChar(1); - m_headerData[35U] = header.getMyCall1().GetChar(2); + m_headerData[31U] = header.getYourCall()[6]; + m_headerData[32U] = header.getYourCall()[7]; + m_headerData[33U] = header.getMyCall1()[0]; + m_headerData[34U] = header.getMyCall1()[1]; + m_headerData[35U] = header.getMyCall1()[2]; m_headerData[36U] = SLOW_DATA_TYPE_HEADER | 5U; - m_headerData[37U] = header.getMyCall1().GetChar(3); - m_headerData[38U] = header.getMyCall1().GetChar(4); - m_headerData[39U] = header.getMyCall1().GetChar(5); - m_headerData[40U] = header.getMyCall1().GetChar(6); - m_headerData[41U] = header.getMyCall1().GetChar(7); + m_headerData[37U] = header.getMyCall1()[3]; + m_headerData[38U] = header.getMyCall1()[4]; + m_headerData[39U] = header.getMyCall1()[5]; + m_headerData[40U] = header.getMyCall1()[6]; + m_headerData[41U] = header.getMyCall1()[7]; m_headerData[42U] = SLOW_DATA_TYPE_HEADER | 5U; - m_headerData[43U] = header.getMyCall2().GetChar(0); - m_headerData[44U] = header.getMyCall2().GetChar(1); - m_headerData[45U] = header.getMyCall2().GetChar(2); - m_headerData[46U] = header.getMyCall2().GetChar(3); + m_headerData[43U] = header.getMyCall2()[0]; + m_headerData[44U] = header.getMyCall2()[1]; + m_headerData[45U] = header.getMyCall2()[2]; + m_headerData[46U] = header.getMyCall2()[3]; CCCITTChecksumReverse cksum; cksum.update(m_headerData + 1U, 5U); @@ -157,41 +160,40 @@ void CSlowDataEncoder::setHeaderData(const CHeaderData& header) m_headerPtr = 0U; } -void CSlowDataEncoder::setTextData(const wxString& text) +void CSlowDataEncoder::setTextData(const std::string& text) { - if (text.IsEmpty()) + if (text.empty()) return; ::memset(m_textData, 'f', SLOW_DATA_FULL_BLOCK_SIZE); m_textData[0U] = SLOW_DATA_TYPE_TEXT | 0U; - m_textData[1U] = text.GetChar(0); - m_textData[2U] = text.GetChar(1); - m_textData[3U] = text.GetChar(2); - m_textData[4U] = text.GetChar(3); - m_textData[5U] = text.GetChar(4); + m_textData[1U] = text[0]; + m_textData[2U] = text[1]; + m_textData[3U] = text[2]; + m_textData[4U] = text[3]; + m_textData[5U] = text[4]; m_textData[6U] = SLOW_DATA_TYPE_TEXT | 1U; - m_textData[7U] = text.GetChar(5); - m_textData[8U] = text.GetChar(6); - m_textData[9U] = text.GetChar(7); - m_textData[10U] = text.GetChar(8); - m_textData[11U] = text.GetChar(9); + m_textData[7U] = text[5]; + m_textData[8U] = text[6]; + m_textData[9U] = text[7]; + m_textData[10U] = text[8]; + m_textData[11U] = text[9]; m_textData[12U] = SLOW_DATA_TYPE_TEXT | 2U; - m_textData[13U] = text.GetChar(10); - m_textData[14U] = text.GetChar(11); - m_textData[15U] = text.GetChar(12); - m_textData[16U] = text.GetChar(13); - m_textData[17U] = text.GetChar(14); + m_textData[13U] = text[10]; + m_textData[14U] = text[11]; + m_textData[15U] = text[12]; + m_textData[16U] = text[13]; + m_textData[17U] = text[14]; m_textData[18U] = SLOW_DATA_TYPE_TEXT | 3U; - m_textData[19U] = text.GetChar(15); - m_textData[20U] = text.GetChar(16); - m_textData[21U] = text.GetChar(17); - m_textData[22U] = text.GetChar(18); - m_textData[23U] = text.GetChar(19); + m_textData[19U] = text[15]; + m_textData[20U] = text[16]; + m_textData[21U] = text[17]; + m_textData[22U] = text[18]; + m_textData[23U] = text[19]; m_textPtr = 0U; } - diff --git a/Common/SlowDataEncoder.h b/Common/SlowDataEncoder.h index f3c534e..4472d2d 100644 --- a/Common/SlowDataEncoder.h +++ b/Common/SlowDataEncoder.h @@ -16,27 +16,48 @@ #include "HeaderData.h" -#include +#include +/* + * Encodes D-Star slow data into the 3-byte data field of each DV frame. + * + * Two independent payloads can be encoded: + * Header — a copy of the radio header, split across 8 tagged 6-byte blocks + * with a CCITT-16 checksum appended. Used by the repeater to + * re-broadcast the header to late-joining receivers. + * Text — a 20-character free-text message split across 4 tagged blocks. + * + * Each block is 6 bytes: 1-byte type/sequence tag followed by 5 payload bytes. + * getHeaderData() and getTextData() each return 3 bytes (half a block) per + * call, cycling back to the start after all blocks have been emitted. + * + * Before writing to the DV frame the 3 bytes are XOR-scrambled with the + * repeating key SCRAMBLER_BYTE1/SCRAMBLER_BYTE2/SCRAMBLER_BYTE3. sync() + * resets the read pointer to the beginning of the sequence so that the + * encoder is aligned with the data-sync frame position in the stream. + */ class CSlowDataEncoder { public: CSlowDataEncoder(); ~CSlowDataEncoder(); void setHeaderData(const CHeaderData& header); - void setTextData(const wxString& text); + void setTextData(const std::string& text); + // Returns the next 3 scrambled bytes of the header slow-data sequence. void getHeaderData(unsigned char* data); + // Returns the next 3 scrambled bytes of the text slow-data sequence. void getTextData(unsigned char* data); void reset(); + // Resets read pointers to the start of the sequence (call on each sync frame). void sync(); private: - unsigned char* m_headerData; - unsigned char* m_textData; - unsigned int m_headerPtr; - unsigned int m_textPtr; + unsigned char* m_headerData; // Pre-packed, unscrambled header slow-data bytes. + unsigned char* m_textData; // Pre-packed, unscrambled text slow-data bytes. + unsigned int m_headerPtr; // Current read offset into m_headerData. + unsigned int m_textPtr; // Current read offset into m_textData. }; #endif diff --git a/Common/SoundCardController.cpp b/Common/SoundCardController.cpp index c69f33d..b87a3f7 100644 --- a/Common/SoundCardController.cpp +++ b/Common/SoundCardController.cpp @@ -19,6 +19,13 @@ #include "CCITTChecksumReverse.h" #include "SoundCardController.h" #include "DStarDefines.h" +#include "Logger.h" + +#include +#include +#include +#include +#include // #define AUDIO_LOOPBACK @@ -33,18 +40,18 @@ const unsigned int MAX_SYNC_BITS = 50U * DV_FRAME_LENGTH_BITS; const unsigned int FEC_SECTION_LENGTH_BITS = 660U; // D-Star bit order version of 0x55 0x55 0x6E 0x0A -const wxUint32 FRAME_SYNC_DATA = 0x00557650U; -const wxUint32 FRAME_SYNC_MASK = 0x00FFFFFFU; +const uint32_t FRAME_SYNC_DATA = 0x00557650U; +const uint32_t FRAME_SYNC_MASK = 0x00FFFFFFU; const unsigned int FRAME_SYNC_ERRS = 2U; // D-Star bit order version of 0x55 0x2D 0x16 -const wxUint32 DATA_SYNC_DATA = 0x00AAB468U; -const wxUint32 DATA_SYNC_MASK = 0x00FFFFFFU; +const uint32_t DATA_SYNC_DATA = 0x00AAB468U; +const uint32_t DATA_SYNC_MASK = 0x00FFFFFFU; const unsigned int DATA_SYNC_ERRS = 2U; // D-Star bit order version of 0x55 0x55 0xC8 0x7A -const wxUint32 END_SYNC_DATA = 0xAAAA135EU; -const wxUint32 END_SYNC_MASK = 0xFFFFFFFFU; +const uint32_t END_SYNC_DATA = 0xAAAA135EU; +const uint32_t END_SYNC_MASK = 0xFFFFFFFFU; const unsigned int END_SYNC_ERRS = 3U; const unsigned char BIT_SYNC = 0xAAU; @@ -149,7 +156,7 @@ const unsigned char INTERLEAVE_TABLE_TX[] = { 0x4AU, 0x07U, 0x4EU, 0x02U, 0x51U, 0x05U, 0x02U, 0x05U, 0x06U, 0x01U, 0x09U, 0x05U, 0x0DU, 0x01U, 0x10U, 0x05U, 0x14U, 0x01U, 0x17U, 0x05U, 0x1BU, 0x01U, 0x1EU, 0x05U, 0x22U, 0x01U, 0x25U, 0x05U, 0x29U, 0x01U, - 0x2CU, 0x05U, 0x30U, 0x00U, 0x33U, 0x03U, 0x36U, 0x06U, 0x3AU, 0x01U, + 0x2CU, 0x05U, 0x30U, 0x00U, 0x33U, 0x03U, 0x36U, 0x06U, 0x3AU, 0x01U, 0x3DU, 0x04U, 0x40U, 0x07U, 0x44U, 0x02U, 0x47U, 0x05U, 0x4BU, 0x00U, 0x4EU, 0x03U, 0x51U, 0x06U, 0x02U, 0x06U, 0x06U, 0x02U, 0x09U, 0x06U, 0x0DU, 0x02U, 0x10U, 0x06U, 0x14U, 0x02U, 0x17U, 0x06U, 0x1BU, 0x02U, @@ -358,7 +365,7 @@ const unsigned char SCRAMBLE_TABLE_RX[] = { 0x7BU, 0x9AU, 0x04U, 0x22U, 0xA3U, 0x6BU, 0x83U, 0x59U, 0x39U, 0x6FU, 0x00U}; -CSoundCardController::CSoundCardController(const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail) : +CSoundCardController::CSoundCardController(const std::string& rxDevice, const std::string& txDevice, bool rxInvert, bool txInvert, float rxLevel, float txLevel, unsigned int txDelay, unsigned int txTail) : CModem(), m_sound(rxDevice, txDevice, DSTAR_RADIO_SAMPLE_RATE, DSTAR_RADIO_BLOCK_SIZE), m_rxLevel(rxLevel), @@ -371,19 +378,19 @@ m_rxState(DSRSCCS_NONE), m_patternBuffer(0x00U), m_demodulator(), m_modulator(), -m_rxBuffer(NULL), +m_rxBuffer(nullptr), m_rxBufferBits(0U), m_dataBits(0U), m_mar(0U), -m_pathMetric(NULL), -m_pathMemory0(NULL), -m_pathMemory1(NULL), -m_pathMemory2(NULL), -m_pathMemory3(NULL), -m_fecOutput(NULL) +m_pathMetric(nullptr), +m_pathMemory0(nullptr), +m_pathMemory1(nullptr), +m_pathMemory2(nullptr), +m_pathMemory3(nullptr), +m_fecOutput(nullptr) { - wxASSERT(!rxDevice.IsEmpty()); - wxASSERT(!txDevice.IsEmpty()); + assert(!rxDevice.empty()); + assert(!txDevice.empty()); m_modulator.setInvert(txInvert); m_demodulator.setInvert(rxInvert); @@ -417,19 +424,17 @@ bool CSoundCardController::start() if (!ret) return false; - Create(); - SetPriority(100U); - Run(); + m_thread = std::thread(&CSoundCardController::entry, this); return true; } -void* CSoundCardController::Entry() +void CSoundCardController::entry() { - wxLogMessage(wxT("Starting Sound Card Controller thread")); + wxLogMessage("Starting Sound Card Controller thread"); while (!m_stopped) { - wxFloat32 val; + float val; while (m_rxAudio.getData(&val, 1U) == 1U) { TRISTATE state = m_demodulator.decode(val * m_rxLevel); switch (state) { @@ -468,21 +473,19 @@ void* CSoundCardController::Entry() } } - Sleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } - wxLogMessage(wxT("Stopping Sound Card Controller thread")); + wxLogMessage("Stopping Sound Card Controller thread"); m_sound.close(); - - return NULL; } bool CSoundCardController::writeHeader(const CHeaderData& header) { bool ret = m_txAudio.hasSpace((m_txDelay + 60U + 85U) * 8U * DSTAR_RADIO_BIT_LENGTH); if (!ret) { - wxLogWarning(wxT("No space to write the header")); + wxLogWarning("No space to write the header"); return false; } @@ -494,25 +497,25 @@ bool CSoundCardController::writeHeader(const CHeaderData& header) buffer1[1U] = header.getFlag2(); buffer1[2U] = header.getFlag3(); - wxString rpt2 = header.getRptCall2(); - for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer1[i + 3U] = rpt2.GetChar(i); + std::string rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer1[i + 3U] = rpt2[i]; - wxString rpt1 = header.getRptCall1(); - for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer1[i + 11U] = rpt1.GetChar(i); + std::string rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer1[i + 11U] = rpt1[i]; - wxString your = header.getYourCall(); - for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer1[i + 19U] = your.GetChar(i); + std::string your = header.getYourCall(); + for (unsigned int i = 0U; i < your.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer1[i + 19U] = your[i]; - wxString my1 = header.getMyCall1(); - for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer1[i + 27U] = my1.GetChar(i); + std::string my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer1[i + 27U] = my1[i]; - wxString my2 = header.getMyCall2(); - for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) - buffer1[i + 35U] = my2.GetChar(i); + std::string my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.size() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer1[i + 35U] = my2[i]; CCCITTChecksumReverse cksum1; cksum1.update(buffer1 + 0U, RADIO_HEADER_LENGTH_BYTES - 2U); @@ -542,7 +545,7 @@ bool CSoundCardController::writeData(const unsigned char* data, unsigned int len bool ret = m_txAudio.hasSpace(tailBlocks * END_PATTERN_LENGTH_BYTES * 8U * DSTAR_RADIO_BIT_LENGTH); if (!ret) { - wxLogWarning(wxT("No space to write end data")); + wxLogWarning("No space to write end data"); return false; } @@ -553,7 +556,7 @@ bool CSoundCardController::writeData(const unsigned char* data, unsigned int len } else { bool ret = m_txAudio.hasSpace(length * 8U * DSTAR_RADIO_BIT_LENGTH); if (!ret) { - wxLogWarning(wxT("No space to write data")); + wxLogWarning("No space to write data"); return false; } @@ -576,14 +579,10 @@ bool CSoundCardController::isTXReady() bool CSoundCardController::isTX() { -#if (defined(__APPLE__) && defined(__MACH__)) || defined(__WINDOWS__) - return m_txAudio.hasData(); -#else - return m_sound.isWriterBusy() || m_txAudio.hasData(); -#endif + return m_sound.isWriterBusy() || m_txAudio.hasData(); } -void CSoundCardController::readCallback(const wxFloat32* input, unsigned int n, int id) +void CSoundCardController::readCallback(const float* input, unsigned int n, int id) { #if !defined(AUDIO_LOOPBACK) if (!m_stopped) @@ -591,12 +590,12 @@ void CSoundCardController::readCallback(const wxFloat32* input, unsigned int n, #endif } -void CSoundCardController::writeCallback(wxFloat32* output, int& n, int id) +void CSoundCardController::writeCallback(float* output, int& n, int id) { - if (n == 0U) - return; + if (n == 0U) + return; - ::memset(output, 0x00, n * sizeof(wxFloat32)); + ::memset(output, 0x00, n * sizeof(float)); if (!m_stopped) { n = m_txAudio.getData(output, n); @@ -691,7 +690,7 @@ void CSoundCardController::txHeader(const unsigned char* in, unsigned char* out) void CSoundCardController::writeBits(unsigned char c) { - wxFloat32 buffer[DSTAR_RADIO_BIT_LENGTH]; + float buffer[DSTAR_RADIO_BIT_LENGTH]; unsigned char mask = 0x01U; for (unsigned int i = 0U; i < 8U; i++) { @@ -711,7 +710,7 @@ void CSoundCardController::writeBits(unsigned char c) void CSoundCardController::processNone(bool bit) { m_patternBuffer <<= 1; - if (bit) + if (bit) m_patternBuffer |= 0x01U; // Exact matching of the frame sync sequence @@ -732,7 +731,7 @@ void CSoundCardController::processNone(bool bit) // Lock the GMSK PLL to this signal m_demodulator.lock(true); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_DATA; @@ -762,12 +761,12 @@ void CSoundCardController::processHeader(bool bit) // A full FEC header if (m_rxBufferBits == FEC_SECTION_LENGTH_BITS) { - // Process the scrambling, interleaving and FEC, then return if the chcksum was correct + // Process the scrambling, interleaving and FEC, then return if the checksum was correct unsigned char header[RADIO_HEADER_LENGTH_BYTES]; bool ok = rxHeader(m_rxBuffer, header); if (ok) { // The checksum is correct - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_HEADER; @@ -806,7 +805,7 @@ void CSoundCardController::processData(bool bit) // Release the GMSK PLL m_demodulator.lock(false); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; @@ -834,7 +833,7 @@ void CSoundCardController::processData(bool bit) // Release the GMSK PLL m_demodulator.lock(false); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_LOST; @@ -848,8 +847,8 @@ void CSoundCardController::processData(bool bit) // Check to see if the sync is arriving late if (m_rxBufferBits == DV_FRAME_LENGTH_BITS && !syncSeen) { for (unsigned int i = 1U; i <= 3U; i++) { - wxUint32 syncMask = DATA_SYNC_MASK >> i; - wxUint32 syncData = DATA_SYNC_DATA >> i; + uint32_t syncMask = DATA_SYNC_MASK >> i; + uint32_t syncData = DATA_SYNC_DATA >> i; errs = countBits((m_patternBuffer & syncMask) ^ syncData); if (errs <= DATA_SYNC_ERRS) { m_rxBufferBits -= i; @@ -866,7 +865,7 @@ void CSoundCardController::processData(bool bit) m_rxBuffer[11U] = DATA_SYNC_BYTES[2U]; } - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_DATA; @@ -881,14 +880,14 @@ void CSoundCardController::processData(bool bit) } } -unsigned int CSoundCardController::countBits(wxUint32 num) +unsigned int CSoundCardController::countBits(uint32_t num) { - unsigned int count = 0U; + unsigned int count = 0U; - for (unsigned int i = 0U; i < 8U; i++) - count += NIBBLE_BITS[(num >> (i * 4U)) & 0x0FU]; + for (unsigned int i = 0U; i < 8U; i++) + count += NIBBLE_BITS[(num >> (i * 4U)) & 0x0FU]; - return count; + return count; } bool CSoundCardController::rxHeader(unsigned char* in, unsigned char* out) @@ -1017,7 +1016,7 @@ void CSoundCardController::acs(int* metric) m_pathMemory2[j] |= BIT_MASK_TABLE1[k]; // Pres. state = S3, Prev. state = S1 & S3 - m1 = metric[3U] + m_pathMetric[1U]; + m1 = metric[3U] + m_pathMetric[1U]; m2 = metric[7U] + m_pathMetric[3U]; tempMetric[3U] = m1 < m2 ? m1 : m2; if (m1 < m2) @@ -1030,7 +1029,7 @@ void CSoundCardController::acs(int* metric) m_mar++; } - + void CSoundCardController::viterbiDecode(int* data) { int metric[8U]; @@ -1068,21 +1067,21 @@ void CSoundCardController::traceBack() if (READ_BIT1(m_pathMemory1, i) == 0) j = 0U; else - j = 2U; + j = 2U; WRITE_BIT1(m_fecOutput, k, 1); k++; - break; + break; - case 2: // if state = S1 + case 2: // if state = S2 if (READ_BIT1(m_pathMemory2, i) == 0) - j = 1U; + j = 1U; else j = 3U; WRITE_BIT1(m_fecOutput, k, 0); k++; break; - case 3U: // if state = S1 + case 3U: // if state = S3 if (READ_BIT1(m_pathMemory3, i) == 0) j = 1U; else diff --git a/Common/SoundCardController.h b/Common/SoundCardController.h index 44da8a5..d1d8f47 100644 --- a/Common/SoundCardController.h +++ b/Common/SoundCardController.h @@ -26,8 +26,10 @@ #include "RingBuffer.h" #include "Modem.h" #include "Utils.h" +#include "StdCompat.h" -#include +#include +#include enum DSRSCC_STATE { DSRSCCS_NONE, @@ -35,13 +37,46 @@ enum DSRSCC_STATE { DSRSCCS_DATA }; +/* + * CSoundCardController - D-Star modem using a sound card as the RF interface. + * + * Implements both CModem (repeater-facing) and IAudioCallback (audio I/O facing). + * The sound card carries the baseband GMSK signal: the discriminator output of the + * radio feeds the RX input, and the TX output drives the radio's microphone input. + * + * Audio path (IAudioCallback): + * readCallback() - called by CSoundCardReaderWriter with captured float samples. + * Appends to m_rxAudio ring buffer; entry() drains it. + * writeCallback() - called when the audio output device needs a new block. + * Drains m_txAudio into the provided output buffer. + * + * Demodulation (entry() thread, processes m_rxAudio bit by bit): + * m_demodulator converts float samples to a recovered bit stream. + * A 32-bit shift register (m_patternBuffer) is used to detect the D-Star + * frame sync pattern (FRAME_SYNC_DATA) and data sync (DATA_SYNC_DATA). + * State machine (m_rxState): NONE -> HEADER -> DATA + * processNone(): hunts for the frame sync pattern (up to MAX_SYNC_BITS). + * processHeader(): accumulates FEC_SECTION_LENGTH_BITS (660) bits, then calls + * rxHeader() which runs the Viterbi decoder and validates + * the CCITT checksum before queuing a DSMTT_HEADER record. + * processData(): accumulates DV_FRAME_LENGTH_BITS per frame; detects end + * pattern and data sync for sequence tracking. + * + * Modulation (writeCallback()): + * writeHeader() / writeData() push into m_txAudio. + * The modulator (m_modulator) converts binary frames to float samples. + * txDelay pads silence before the first header bit; txTail pads after EOT. + * + * FEC and interleaving: + * The header section is rate-1/2 convolutional coded + interleaved. + * Viterbi decoding state is held in m_pathMetric, m_pathMemory0-3, m_fecOutput. + * countBits() uses a 4-bit lookup table (NIBBLE_BITS) for Hamming weight. + */ class CSoundCardController : public CModem, public IAudioCallback { public: - CSoundCardController(const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail); + CSoundCardController(const std::string& rxDevice, const std::string& txDevice, bool rxInvert, bool txInvert, float rxLevel, float txLevel, unsigned int txDelay, unsigned int txTail); virtual ~CSoundCardController(); - virtual void* Entry(); - virtual bool start(); virtual unsigned int getSpace(); @@ -51,19 +86,21 @@ public: virtual bool writeHeader(const CHeaderData& header); virtual bool writeData(const unsigned char* data, unsigned int length, bool end); - virtual void readCallback(const wxFloat32* input, unsigned int n, int id); - virtual void writeCallback(wxFloat32* output, int& n, int id); + virtual void readCallback(const float* input, unsigned int n, int id); + virtual void writeCallback(float* output, int& n, int id); private: + void entry(); + CSoundCardReaderWriter m_sound; - wxFloat32 m_rxLevel; - wxFloat32 m_txLevel; + float m_rxLevel; + float m_txLevel; unsigned int m_txDelay; unsigned int m_txTail; - CRingBuffer m_txAudio; - CRingBuffer m_rxAudio; + CRingBuffer m_txAudio; + CRingBuffer m_rxAudio; DSRSCC_STATE m_rxState; - wxUint32 m_patternBuffer; + uint32_t m_patternBuffer; CDStarGMSKDemodulator m_demodulator; CDStarGMSKModulator m_modulator; unsigned char* m_rxBuffer; @@ -89,8 +126,7 @@ private: void txHeader(const unsigned char* in, unsigned char* out); void writeBits(unsigned char c); - unsigned int countBits(wxUint32 num); + unsigned int countBits(uint32_t num); }; #endif - diff --git a/Common/SoundCardReaderWriter.cpp b/Common/SoundCardReaderWriter.cpp index 9cbfab3..d189569 100644 --- a/Common/SoundCardReaderWriter.cpp +++ b/Common/SoundCardReaderWriter.cpp @@ -18,295 +18,44 @@ */ #include "SoundCardReaderWriter.h" +#include "Logger.h" -#if (defined(__APPLE__) && defined(__MACH__)) || defined(__WINDOWS__) +#include +#include +#include +#include +#include -static int scrwCallback(const void* input, void* output, unsigned long nSamples, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData) -{ - wxASSERT(userData != NULL); - - CSoundCardReaderWriter* object = reinterpret_cast(userData); - - object->callback(static_cast(input), static_cast(output), nSamples); +#if !defined(_WIN32) && !(defined(__APPLE__) && defined(__MACH__)) - return paContinue; -} +std::vector CSoundCardReaderWriter::m_readDevices; +std::vector CSoundCardReaderWriter::m_writeDevices; -CSoundCardReaderWriter::CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize) : +CSoundCardReaderWriter::CSoundCardReaderWriter(const std::string& readDevice, const std::string& writeDevice, unsigned int sampleRate, unsigned int blockSize) : m_readDevice(readDevice), m_writeDevice(writeDevice), m_sampleRate(sampleRate), m_blockSize(blockSize), -m_callback(NULL), +m_callback(nullptr), m_id(-1), -m_stream(NULL) +m_reader(nullptr), +m_writer(nullptr) { + assert(sampleRate > 0U); + assert(blockSize > 0U); } CSoundCardReaderWriter::~CSoundCardReaderWriter() { } -wxArrayString CSoundCardReaderWriter::getReadDevices() -{ - wxArrayString devices; - - devices.Alloc(10); - - PaError error = ::Pa_Initialize(); - if (error != paNoError) - return devices; - -#if defined(__WINDOWS__) - PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound); -#elif defined(__APPLE__) && defined(__MACH__) - PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio); -#else - PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA); -#endif - if (apiIndex == paHostApiNotFound) { - ::Pa_Terminate(); - return devices; - } - - PaDeviceIndex n = ::Pa_GetDeviceCount(); - if (n <= 0) { - ::Pa_Terminate(); - return devices; - } - - for (PaDeviceIndex i = 0; i < n; i++) { - const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i); - - if (device->hostApi != apiIndex) - continue; - - if (device->maxInputChannels > 0) { - wxString name(device->name, wxConvLocal); - devices.Add(name); - } - } - - ::Pa_Terminate(); - - return devices; -} - -wxArrayString CSoundCardReaderWriter::getWriteDevices() -{ - wxArrayString devices; - - devices.Alloc(10); - - PaError error = ::Pa_Initialize(); - if (error != paNoError) - return devices; - -#if defined(__WINDOWS__) - PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound); -#elif defined(__APPLE__) && defined(__MACH__) - PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio); -#else - PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA); -#endif - if (apiIndex == paHostApiNotFound) { - ::Pa_Terminate(); - return devices; - } - - PaDeviceIndex n = ::Pa_GetDeviceCount(); - if (n <= 0) { - ::Pa_Terminate(); - return devices; - } - - for (PaDeviceIndex i = 0; i < n; i++) { - const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i); - - if (device->hostApi != apiIndex) - continue; - - if (device->maxOutputChannels > 0) { - wxString name(device->name, wxConvLocal); - devices.Add(name); - } - } - - ::Pa_Terminate(); - - return devices; -} - -void CSoundCardReaderWriter::setCallback(IAudioCallback* callback, int id) -{ - wxASSERT(callback != NULL); - - m_callback = callback; - m_id = id; -} - -bool CSoundCardReaderWriter::open() -{ - PaError error = ::Pa_Initialize(); - if (error != paNoError) { - wxLogError(wxT("Cannot initialise PortAudio")); - return false; - } - - PaStreamParameters* pParamsIn = NULL; - PaStreamParameters* pParamsOut = NULL; - - PaStreamParameters paramsIn; - PaStreamParameters paramsOut; - - PaDeviceIndex inDev, outDev; - bool res = convertNameToDevices(inDev, outDev); - if (!res) { - wxLogError(wxT("Cannot convert name to device")); - return false; - } - - if (inDev != -1) { - const PaDeviceInfo* inInfo = ::Pa_GetDeviceInfo(inDev); - if (inInfo == NULL) { - wxLogError(wxT("Cannot get device information for the input device")); - return false; - } - - paramsIn.device = inDev; - paramsIn.channelCount = 1; - paramsIn.sampleFormat = paFloat32; - paramsIn.hostApiSpecificStreamInfo = NULL; - paramsIn.suggestedLatency = inInfo->defaultLowInputLatency; - - pParamsIn = ¶msIn; - } - - if (outDev != -1) { - const PaDeviceInfo* outInfo = ::Pa_GetDeviceInfo(outDev); - if (outInfo == NULL) { - wxLogError(wxT("Cannot get device information for the output device")); - return false; - } - - paramsOut.device = outDev; - paramsOut.channelCount = 1; - paramsOut.sampleFormat = paFloat32; - paramsOut.hostApiSpecificStreamInfo = NULL; - paramsOut.suggestedLatency = outInfo->defaultLowOutputLatency; - - pParamsOut = ¶msOut; - } - - error = ::Pa_OpenStream(&m_stream, pParamsIn, pParamsOut, double(m_sampleRate), m_blockSize, paNoFlag, &scrwCallback, this); - if (error != paNoError) { - wxLogError(wxT("Cannot open the audios stream(s)")); - ::Pa_Terminate(); - return false; - } - - error = ::Pa_StartStream(m_stream); - if (error != paNoError) { - wxLogError(wxT("Cannot start the audio stream(s)")); - ::Pa_CloseStream(m_stream); - m_stream = NULL; - - ::Pa_Terminate(); - return false; - } - - return true; -} - -void CSoundCardReaderWriter::close() -{ - wxASSERT(m_stream != NULL); - - ::Pa_AbortStream(m_stream); - - ::Pa_CloseStream(m_stream); - - ::Pa_Terminate(); -} - -void CSoundCardReaderWriter::callback(const wxFloat32* input, wxFloat32* output, unsigned int nSamples) -{ - if (m_callback != NULL) { - m_callback->readCallback(input, nSamples, m_id); - int count = int(nSamples); - m_callback->writeCallback(output, count, m_id); - } -} - -bool CSoundCardReaderWriter::convertNameToDevices(PaDeviceIndex& inDev, PaDeviceIndex& outDev) -{ - inDev = outDev = -1; - -#if defined(__WINDOWS__) - PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound); -#elif defined(__APPLE__) && defined(__MACH__) - PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio); -#else - PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA); -#endif - if (apiIndex == paHostApiNotFound) - return false; - - PaDeviceIndex n = ::Pa_GetDeviceCount(); - if (n <= 0) - return false; - - for (PaDeviceIndex i = 0; i < n; i++) { - const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i); - - if (device->hostApi != apiIndex) - continue; - - wxString name(device->name, wxConvLocal); - - if (!m_readDevice.IsEmpty() && m_readDevice.IsSameAs(name) && device->maxInputChannels > 0) - inDev = i; - - if (!m_writeDevice.IsEmpty() && m_writeDevice.IsSameAs(name) && device->maxOutputChannels > 0) - outDev = i; - } - - if (inDev == -1 && outDev == -1) - return false; - - return true; -} - -#else - -wxArrayString CSoundCardReaderWriter::m_readDevices; -wxArrayString CSoundCardReaderWriter::m_writeDevices; - -CSoundCardReaderWriter::CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize) : -m_readDevice(readDevice), -m_writeDevice(writeDevice), -m_sampleRate(sampleRate), -m_blockSize(blockSize), -m_callback(NULL), -m_id(-1), -m_reader(NULL), -m_writer(NULL) -{ - wxASSERT(sampleRate > 0U); - wxASSERT(blockSize > 0U); -} - -CSoundCardReaderWriter::~CSoundCardReaderWriter() +std::vector CSoundCardReaderWriter::getReadDevices() { -} - -wxArrayString CSoundCardReaderWriter::getReadDevices() -{ - snd_ctl_t *handle = NULL; - snd_pcm_t *pcm = NULL; + snd_ctl_t *handle = nullptr; + snd_pcm_t *pcm = nullptr; char NameString[256]; - wxArrayString devices(m_readDevices); + std::vector devices(m_readDevices); snd_ctl_card_info_t* info; snd_ctl_card_info_alloca(&info); @@ -324,13 +73,13 @@ wxArrayString CSoundCardReaderWriter::getReadDevices() int card = -1; while (::snd_card_next(&card) == 0 && card >= 0) { char hwdev[80]; - ::sprintf(hwdev, "hw:%d", card); + ::snprintf(hwdev, sizeof(hwdev), "hw:%d", card); if (::snd_ctl_open(&handle, hwdev, 0) < 0) continue; ::snd_ctl_card_info(handle, info); - ::snd_ctl_card_info_get_name(info); + ::snd_ctl_card_info_get_name(info); int dev = -1; while (::snd_ctl_pcm_next_device(handle, &dev) == 0 && dev >= 0) { @@ -340,7 +89,7 @@ wxArrayString CSoundCardReaderWriter::getReadDevices() err = ::snd_ctl_pcm_info(handle, pcminfo); if (err != -ENOENT) { - ::sprintf(hwdev, "hw:%d,%d", card, dev); + ::snprintf(hwdev, sizeof(hwdev), "hw:%d,%d", card, dev); if (::snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK) < 0) continue; @@ -349,35 +98,34 @@ wxArrayString CSoundCardReaderWriter::getReadDevices() ::snd_pcm_hw_params_get_channels_min(pars, &min); ::snd_pcm_hw_params_get_channels_max(pars, &max); - ::snd_pcm_hw_params_get_rate_min(pars, &min, NULL); - ::snd_pcm_hw_params_get_rate_max(pars, &max, NULL); - - ::sprintf(NameString, "hw:%d,%d %s(%s)", + ::snd_pcm_hw_params_get_rate_min(pars, &min, nullptr); + ::snd_pcm_hw_params_get_rate_max(pars, &max, nullptr); + + ::snprintf(NameString, sizeof(NameString), "hw:%d,%d %s(%s)", card, dev, ::snd_pcm_info_get_name(pcminfo), snd_ctl_card_info_get_name(info)); - wxString name(NameString, wxConvLocal); - devices.Add(name); + devices.push_back(std::string(NameString)); ::snd_pcm_close(pcm); - pcm = NULL; + pcm = nullptr; } - } + } - ::snd_ctl_close(handle); + ::snd_ctl_close(handle); } return devices; } -wxArrayString CSoundCardReaderWriter::getWriteDevices() +std::vector CSoundCardReaderWriter::getWriteDevices() { - snd_ctl_t *handle = NULL; - snd_pcm_t *pcm = NULL; + snd_ctl_t *handle = nullptr; + snd_pcm_t *pcm = nullptr; char NameString[256]; - wxArrayString devices(m_writeDevices); + std::vector devices(m_writeDevices); snd_ctl_card_info_t* info; snd_ctl_card_info_alloca(&info); @@ -391,52 +139,51 @@ wxArrayString CSoundCardReaderWriter::getWriteDevices() unsigned min, max; int err; snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; - + int card = -1; while (::snd_card_next(&card) == 0 && card >= 0) { char hwdev[80]; - ::sprintf(hwdev, "hw:%d", card); + ::snprintf(hwdev, sizeof(hwdev), "hw:%d", card); if (::snd_ctl_open(&handle, hwdev, 0) < 0) continue; ::snd_ctl_card_info(handle, info); - ::snd_ctl_card_info_get_name(info); + ::snd_ctl_card_info_get_name(info); - int dev = -1; + int dev = -1; while (::snd_ctl_pcm_next_device(handle, &dev) == 0 && dev >= 0) { ::snd_pcm_info_set_device(pcminfo, dev); ::snd_pcm_info_set_subdevice(pcminfo, 0); ::snd_pcm_info_set_stream(pcminfo, stream); - err= ::snd_ctl_pcm_info(handle, pcminfo); + err = ::snd_ctl_pcm_info(handle, pcminfo); if (err != -ENOENT) { - ::sprintf(hwdev, "hw:%d,%d", card, dev); + ::snprintf(hwdev, sizeof(hwdev), "hw:%d,%d", card, dev); - if (::snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK) < 0) - continue; + if (::snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK) < 0) + continue; + + ::snd_pcm_hw_params_any(pcm, pars); + ::snd_pcm_hw_params_get_channels_min(pars, &min); + ::snd_pcm_hw_params_get_channels_max(pars, &max); - ::snd_pcm_hw_params_any(pcm, pars); - ::snd_pcm_hw_params_get_channels_min(pars, &min); - ::snd_pcm_hw_params_get_channels_max(pars, &max); - - ::snd_pcm_hw_params_get_rate_min(pars, &min, NULL); - ::snd_pcm_hw_params_get_rate_max(pars, &max, NULL); + ::snd_pcm_hw_params_get_rate_min(pars, &min, nullptr); + ::snd_pcm_hw_params_get_rate_max(pars, &max, nullptr); - ::sprintf(NameString, "hw:%d,%d %s(%s)", - card, dev, + ::snprintf(NameString, sizeof(NameString), "hw:%d,%d %s(%s)", + card, dev, ::snd_pcm_info_get_name(pcminfo), ::snd_ctl_card_info_get_name(info)); - wxString name(NameString, wxConvLocal); - devices.Add(name); + devices.push_back(std::string(NameString)); - ::snd_pcm_close(pcm); - pcm = NULL; + ::snd_pcm_close(pcm); + pcm = nullptr; } - } + } - ::snd_ctl_close(handle); + ::snd_ctl_close(handle); } return devices; @@ -444,7 +191,7 @@ wxArrayString CSoundCardReaderWriter::getWriteDevices() void CSoundCardReaderWriter::setCallback(IAudioCallback* callback, int id) { - wxASSERT(callback != NULL); + assert(callback != nullptr); m_callback = callback; @@ -460,11 +207,13 @@ bool CSoundCardReaderWriter::open() char* ptr; // Store the opened devices because ALSA won't enumerate them - m_readDevices.Add(m_readDevice); - m_writeDevices.Add(m_writeDevice); + m_readDevices.push_back(m_readDevice); + m_writeDevices.push_back(m_writeDevice); - ::strcpy(buf1, (const char*)m_writeDevice.mb_str(wxConvUTF8)); - ::strcpy(buf2, (const char*)m_readDevice.mb_str(wxConvUTF8)); + ::strncpy(buf1, m_writeDevice.c_str(), sizeof(buf1) - 1); + buf1[sizeof(buf1) - 1] = '\0'; + ::strncpy(buf2, m_readDevice.c_str(), sizeof(buf2) - 1); + buf2[sizeof(buf2) - 1] = '\0'; ptr = ::strchr(buf1, ' '); if (ptr) *ptr = 0; // Get Device part of name @@ -472,44 +221,38 @@ bool CSoundCardReaderWriter::open() ptr = ::strchr(buf2, ' '); if (ptr) *ptr = 0; // Get Device part of name - wxString writeDevice(buf1, wxConvLocal); - wxString readDevice(buf2, wxConvLocal); + std::string writeDevice(buf1); + std::string readDevice(buf2); - snd_pcm_t* playHandle = NULL; + snd_pcm_t* playHandle = nullptr; if ((err = ::snd_pcm_open(&playHandle, buf1, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot open playback audio device %s (%s)"), writeDevice.c_str(), error.c_str()); + wxLogError("Cannot open playback audio device %s (%s)", writeDevice.c_str(), ::snd_strerror(err)); return false; } snd_pcm_hw_params_t* hw_params; if ((err = ::snd_pcm_hw_params_malloc(&hw_params)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot allocate hardware parameter structure (%s)"), error.c_str()); + wxLogError("Cannot allocate hardware parameter structure (%s)", ::snd_strerror(err)); return false; } if ((err = ::snd_pcm_hw_params_any(playHandle, hw_params)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot initialize hardware parameter structure (%s)"), error.c_str()); + wxLogError("Cannot initialize hardware parameter structure (%s)", ::snd_strerror(err)); return false; } if ((err = ::snd_pcm_hw_params_set_access(playHandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot set access type (%s)"), error.c_str()); + wxLogError("Cannot set access type (%s)", ::snd_strerror(err)); return false; } if ((err = ::snd_pcm_hw_params_set_format(playHandle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot set sample format (%s)"), error.c_str()); + wxLogError("Cannot set sample format (%s)", ::snd_strerror(err)); return false; } - + if ((err = ::snd_pcm_hw_params_set_rate(playHandle, hw_params, m_sampleRate, 0)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot set sample rate (%s)"), error.c_str()); + wxLogError("Cannot set sample rate (%s)", ::snd_strerror(err)); return false; } @@ -519,87 +262,75 @@ bool CSoundCardReaderWriter::open() playChannels = 2U; if ((err = ::snd_pcm_hw_params_set_channels(playHandle, hw_params, 2)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot play set channel count (%s)"), error.c_str()); + wxLogError("Cannot play set channel count (%s)", ::snd_strerror(err)); return false; } } - + if ((err = ::snd_pcm_hw_params(playHandle, hw_params)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot set parameters (%s)"), error.c_str()); + wxLogError("Cannot set parameters (%s)", ::snd_strerror(err)); return false; } - + ::snd_pcm_hw_params_free(hw_params); - + if ((err = ::snd_pcm_prepare(playHandle)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot prepare audio interface for use (%s)"), error.c_str()); + wxLogError("Cannot prepare audio interface for use (%s)", ::snd_strerror(err)); return false; } // Open Capture - snd_pcm_t* recHandle = NULL; + snd_pcm_t* recHandle = nullptr; if ((err = ::snd_pcm_open(&recHandle, buf2, SND_PCM_STREAM_CAPTURE, 0)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot open capture audio device %s (%s)"), readDevice.c_str(), error.c_str()); + wxLogError("Cannot open capture audio device %s (%s)", readDevice.c_str(), ::snd_strerror(err)); return false; } if ((err = ::snd_pcm_hw_params_malloc(&hw_params)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot allocate hardware parameter structure (%s)"), error.c_str()); + wxLogError("Cannot allocate hardware parameter structure (%s)", ::snd_strerror(err)); return false; } if ((err = ::snd_pcm_hw_params_any(recHandle, hw_params)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot initialize hardware parameter structure (%s)"), error.c_str()); + wxLogError("Cannot initialize hardware parameter structure (%s)", ::snd_strerror(err)); return false; } - + if ((err = ::snd_pcm_hw_params_set_access(recHandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot set access type (%s)"), error.c_str()); + wxLogError("Cannot set access type (%s)", ::snd_strerror(err)); return false; } if ((err = ::snd_pcm_hw_params_set_format(recHandle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot set sample format (%s)"), error.c_str()); + wxLogError("Cannot set sample format (%s)", ::snd_strerror(err)); return false; } - + if ((err = ::snd_pcm_hw_params_set_rate(recHandle, hw_params, m_sampleRate, 0)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot set sample rate (%s)"), error.c_str()); + wxLogError("Cannot set sample rate (%s)", ::snd_strerror(err)); return false; } - + unsigned int recChannels = 1U; - + if ((err = ::snd_pcm_hw_params_set_channels(recHandle, hw_params, 1)) < 0) { recChannels = 2U; - if ((err = ::snd_pcm_hw_params_set_channels (recHandle, hw_params, 2)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot rec set channel count (%s)"), error.c_str()); + if ((err = ::snd_pcm_hw_params_set_channels(recHandle, hw_params, 2)) < 0) { + wxLogError("Cannot rec set channel count (%s)", ::snd_strerror(err)); return false; } } - + if ((err = ::snd_pcm_hw_params(recHandle, hw_params)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot set parameters (%s)"), error.c_str()); + wxLogError("Cannot set parameters (%s)", ::snd_strerror(err)); return false; } ::snd_pcm_hw_params_free(hw_params); if ((err = ::snd_pcm_prepare(recHandle)) < 0) { - wxString error(::snd_strerror(err), wxConvLocal); - wxLogError(wxT("Cannot prepare audio interface for use (%s)"), error.c_str()); + wxLogError("Cannot prepare audio interface for use (%s)", ::snd_strerror(err)); return false; } @@ -607,27 +338,28 @@ bool CSoundCardReaderWriter::open() for (unsigned int i = 0U; i < 10U; ++i) ::snd_pcm_readi(recHandle, samples, 128); - wxLogMessage(wxT("Opened %s %s Rate %u"), writeDevice.c_str(), readDevice.c_str(), m_sampleRate); + wxLogMessage("Opened %s %s Rate %u", writeDevice.c_str(), readDevice.c_str(), m_sampleRate); m_reader = new CSoundCardReader(recHandle, m_blockSize, recChannels, m_callback, m_id); m_writer = new CSoundCardWriter(playHandle, m_blockSize, playChannels, m_callback, m_id); - m_reader->Create(); - m_reader->Run(); - - m_writer->Create(); - m_writer->Run(); + m_reader->start(); + m_writer->start(); - return true; + return true; } void CSoundCardReaderWriter::close() { - m_reader->kill(); - m_writer->kill(); + if (m_reader != nullptr) { + m_reader->kill(); + m_reader->join(); + } - m_reader->Wait(); - m_writer->Wait(); + if (m_writer != nullptr) { + m_writer->kill(); + m_writer->join(); + } } bool CSoundCardReaderWriter::isWriterBusy() const @@ -636,22 +368,22 @@ bool CSoundCardReaderWriter::isWriterBusy() const } CSoundCardReader::CSoundCardReader(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id) : -wxThread(wxTHREAD_JOINABLE), m_handle(handle), m_blockSize(blockSize), m_channels(channels), m_callback(callback), m_id(id), m_killed(false), -m_buffer(NULL), -m_samples(NULL) +m_buffer(nullptr), +m_samples(nullptr), +m_thread() { - wxASSERT(handle != NULL); - wxASSERT(blockSize > 0U); - wxASSERT(channels == 1U || channels == 2U); - wxASSERT(callback != NULL); + assert(handle != nullptr); + assert(blockSize > 0U); + assert(channels == 1U || channels == 2U); + assert(callback != nullptr); - m_buffer = new wxFloat32[blockSize]; + m_buffer = new float[blockSize]; m_samples = new short[2U * blockSize]; } @@ -661,16 +393,20 @@ CSoundCardReader::~CSoundCardReader() delete[] m_samples; } -void* CSoundCardReader::Entry() +void CSoundCardReader::start() +{ + m_thread = std::thread(&CSoundCardReader::entry, this); +} + +void CSoundCardReader::entry() { - wxLogMessage(wxT("Starting ALSA reader thread")); + wxLogMessage("Starting ALSA reader thread"); while (!m_killed) { snd_pcm_sframes_t ret; while ((ret = ::snd_pcm_readi(m_handle, m_samples, m_blockSize)) < 0) { if (ret != -EPIPE) { - wxString error(::snd_strerror(ret), wxConvLocal); - wxLogWarning(wxT("snd_pcm_readi returned %d (%s)"), ret, error.c_str()); + wxLogWarning("snd_pcm_readi returned %d (%s)", (int)ret, ::snd_strerror(ret)); } ::snd_pcm_recover(m_handle, ret, 1); @@ -678,21 +414,19 @@ void* CSoundCardReader::Entry() if (m_channels == 1U) { for (int n = 0; n < ret; n++) - m_buffer[n] = wxFloat32(m_samples[n]) / 32768.0F; + m_buffer[n] = float(m_samples[n]) / 32768.0F; } else { int i = 0; for (int n = 0; n < (ret * 2); n += 2) - m_buffer[i++] = wxFloat32(m_samples[n + 1]) / 32768.0F; + m_buffer[i++] = float(m_samples[n + 1]) / 32768.0F; } m_callback->readCallback(m_buffer, (unsigned int)ret, m_id); } - wxLogMessage(wxT("Stopping ALSA reader thread")); + wxLogMessage("Stopping ALSA reader thread"); ::snd_pcm_close(m_handle); - - return NULL; } void CSoundCardReader::kill() @@ -700,23 +434,29 @@ void CSoundCardReader::kill() m_killed = true; } +void CSoundCardReader::join() +{ + if (m_thread.joinable()) + m_thread.join(); +} + CSoundCardWriter::CSoundCardWriter(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id) : -wxThread(wxTHREAD_JOINABLE), m_handle(handle), m_blockSize(blockSize), m_channels(channels), m_callback(callback), m_id(id), m_killed(false), -m_buffer(NULL), -m_samples(NULL) +m_buffer(nullptr), +m_samples(nullptr), +m_thread() { - wxASSERT(handle != NULL); - wxASSERT(blockSize > 0U); - wxASSERT(channels == 1U || channels == 2U); - wxASSERT(callback != NULL); + assert(handle != nullptr); + assert(blockSize > 0U); + assert(channels == 1U || channels == 2U); + assert(callback != nullptr); - m_buffer = new wxFloat32[2U * blockSize]; + m_buffer = new float[2U * blockSize]; m_samples = new short[4U * blockSize]; } @@ -726,16 +466,21 @@ CSoundCardWriter::~CSoundCardWriter() delete[] m_samples; } -void* CSoundCardWriter::Entry() +void CSoundCardWriter::start() { - wxLogMessage(wxT("Starting ALSA writer thread")); + m_thread = std::thread(&CSoundCardWriter::entry, this); +} + +void CSoundCardWriter::entry() +{ + wxLogMessage("Starting ALSA writer thread"); while (!m_killed) { int nSamples = 2U * m_blockSize; m_callback->writeCallback(m_buffer, nSamples, m_id); if (nSamples == 0U) { - Sleep(5UL); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); } else { if (m_channels == 1U) { for (int n = 0U; n < nSamples; n++) @@ -748,14 +493,13 @@ void* CSoundCardWriter::Entry() m_samples[i++] = sample; // Same value to both channels } } - + int offset = 0U; snd_pcm_sframes_t ret; while ((ret = ::snd_pcm_writei(m_handle, m_samples + offset, nSamples - offset)) != (nSamples - offset)) { if (ret < 0) { if (ret != -EPIPE) { - wxString error(::snd_strerror(ret), wxConvLocal); - wxLogWarning(wxT("snd_pcm_writei returned %d (%s)"), ret, error.c_str()); + wxLogWarning("snd_pcm_writei returned %d (%s)", (int)ret, ::snd_strerror(ret)); } ::snd_pcm_recover(m_handle, ret, 1); @@ -766,11 +510,9 @@ void* CSoundCardWriter::Entry() } } - wxLogMessage(wxT("Stopping ALSA writer thread")); + wxLogMessage("Stopping ALSA writer thread"); ::snd_pcm_close(m_handle); - - return NULL; } void CSoundCardWriter::kill() @@ -778,6 +520,12 @@ void CSoundCardWriter::kill() m_killed = true; } +void CSoundCardWriter::join() +{ + if (m_thread.joinable()) + m_thread.join(); +} + bool CSoundCardWriter::isBusy() const { snd_pcm_state_t state = ::snd_pcm_state(m_handle); @@ -785,4 +533,163 @@ bool CSoundCardWriter::isBusy() const return state == SND_PCM_STATE_RUNNING || state == SND_PCM_STATE_DRAINING; } +#else + +// PortAudio implementation for Windows and macOS + +static int paCallback(const void* input, void* output, unsigned long frameCount, + const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void* userData) +{ + CSoundCardReaderWriter* rw = static_cast(userData); + rw->callback(static_cast(input), static_cast(output), frameCount); + return paContinue; +} + +CSoundCardReaderWriter::CSoundCardReaderWriter(const std::string& readDevice, const std::string& writeDevice, unsigned int sampleRate, unsigned int blockSize) : +m_readDevice(readDevice), +m_writeDevice(writeDevice), +m_sampleRate(sampleRate), +m_blockSize(blockSize), +m_callback(nullptr), +m_id(-1), +m_stream(nullptr) +{ +} + +CSoundCardReaderWriter::~CSoundCardReaderWriter() +{ +} + +void CSoundCardReaderWriter::setCallback(IAudioCallback* callback, int id) +{ + m_callback = callback; + m_id = id; +} + +bool CSoundCardReaderWriter::convertNameToDevices(PaDeviceIndex& inDev, PaDeviceIndex& outDev) +{ + inDev = paNoDevice; + outDev = paNoDevice; + + int count = Pa_GetDeviceCount(); + for (int i = 0; i < count; i++) { + const PaDeviceInfo* info = Pa_GetDeviceInfo(i); + if (info == nullptr) + continue; + + std::string name(info->name); + if (name == m_readDevice && info->maxInputChannels > 0) + inDev = i; + if (name == m_writeDevice && info->maxOutputChannels > 0) + outDev = i; + } + + return inDev != paNoDevice && outDev != paNoDevice; +} + +bool CSoundCardReaderWriter::open() +{ + PaError err = Pa_Initialize(); + if (err != paNoError) { + wxLogError("Pa_Initialize failed: %s", Pa_GetErrorText(err)); + return false; + } + + PaDeviceIndex inDev, outDev; + if (!convertNameToDevices(inDev, outDev)) { + wxLogError("Could not find PortAudio devices: read='%s' write='%s'", + m_readDevice.c_str(), m_writeDevice.c_str()); + Pa_Terminate(); + return false; + } + + PaStreamParameters inParams, outParams; + inParams.device = inDev; + inParams.channelCount = 1; + inParams.sampleFormat = paFloat32; + inParams.suggestedLatency = Pa_GetDeviceInfo(inDev)->defaultLowInputLatency; + inParams.hostApiSpecificStreamInfo = nullptr; + + outParams.device = outDev; + outParams.channelCount = 1; + outParams.sampleFormat = paFloat32; + outParams.suggestedLatency = Pa_GetDeviceInfo(outDev)->defaultLowOutputLatency; + outParams.hostApiSpecificStreamInfo = nullptr; + + err = Pa_OpenStream(&m_stream, &inParams, &outParams, m_sampleRate, m_blockSize, paClipOff, paCallback, this); + if (err != paNoError) { + wxLogError("Pa_OpenStream failed: %s", Pa_GetErrorText(err)); + Pa_Terminate(); + return false; + } + + err = Pa_StartStream(m_stream); + if (err != paNoError) { + wxLogError("Pa_StartStream failed: %s", Pa_GetErrorText(err)); + Pa_CloseStream(m_stream); + m_stream = nullptr; + Pa_Terminate(); + return false; + } + + wxLogMessage("Opened PortAudio: read='%s' write='%s' rate=%u", + m_readDevice.c_str(), m_writeDevice.c_str(), m_sampleRate); + + return true; +} + +void CSoundCardReaderWriter::close() +{ + if (m_stream != nullptr) { + Pa_StopStream(m_stream); + Pa_CloseStream(m_stream); + m_stream = nullptr; + } + + Pa_Terminate(); +} + +void CSoundCardReaderWriter::callback(const float* input, float* output, unsigned int nSamples) +{ + if (m_callback != nullptr) { + m_callback->readCallback(input, nSamples, m_id); + int n = static_cast(nSamples); + m_callback->writeCallback(output, n, m_id); + } +} + +std::vector CSoundCardReaderWriter::getReadDevices() +{ + std::vector devices; + + Pa_Initialize(); + + int count = Pa_GetDeviceCount(); + for (int i = 0; i < count; i++) { + const PaDeviceInfo* info = Pa_GetDeviceInfo(i); + if (info != nullptr && info->maxInputChannels > 0) + devices.push_back(std::string(info->name)); + } + + Pa_Terminate(); + return devices; +} + +std::vector CSoundCardReaderWriter::getWriteDevices() +{ + std::vector devices; + + Pa_Initialize(); + + int count = Pa_GetDeviceCount(); + for (int i = 0; i < count; i++) { + const PaDeviceInfo* info = Pa_GetDeviceInfo(i); + if (info != nullptr && info->maxOutputChannels > 0) + devices.push_back(std::string(info->name)); + } + + Pa_Terminate(); + return devices; +} + #endif diff --git a/Common/SoundCardReaderWriter.h b/Common/SoundCardReaderWriter.h index 0facbbc..b466078 100644 --- a/Common/SoundCardReaderWriter.h +++ b/Common/SoundCardReaderWriter.h @@ -16,35 +16,64 @@ #define SoundCardReaderWriter_H #include "AudioCallback.h" +#include "StdCompat.h" -#include +#include +#include -#if (defined(__APPLE__) && defined(__MACH__)) || defined(__WINDOWS__) +/* + * CSoundCardReaderWriter - Platform-abstracted stereo audio I/O for the sound-card modem. + * + * Two implementations are selected at compile time: + * + * PortAudio (Windows and macOS): + * A single PaStream runs a combined input/output callback (CSoundCardReaderWriter::callback). + * The callback receives captured samples and provides playback samples in the same + * invocation, which gives the lowest latency on those platforms. + * Device enumeration returns human-readable names from the PortAudio device list. + * + * ALSA (Linux): + * Separate CSoundCardReader and CSoundCardWriter objects each own an ALSA PCM handle + * and run in dedicated threads (Reader::entry() / Writer::entry()). + * The reader thread calls IAudioCallback::readCallback() with captured float samples. + * The writer thread calls IAudioCallback::writeCallback() to request output samples. + * Threading allows independent blocking on capture and playback devices, which is + * important when RX and TX use different physical sound cards. + * Device enumeration walks the ALSA control interface (snd_card_next / snd_ctl_pcm_next_device). + * + * In both cases, samples are normalised floats in [-1.0, 1.0]. The ALSA path converts + * to/from signed 16-bit PCM internally. + * + * IAudioCallback::readCallback(input, n, id) - n float samples arrived from device id. + * IAudioCallback::writeCallback(output, n, id) - fill n float samples for device id. + */ + +#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) #include "portaudio.h" class CSoundCardReaderWriter { public: - CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize); + CSoundCardReaderWriter(const std::string& readDevice, const std::string& writeDevice, unsigned int sampleRate, unsigned int blockSize); ~CSoundCardReaderWriter(); void setCallback(IAudioCallback* callback, int id); bool open(); void close(); - void callback(const wxFloat32* input, wxFloat32* output, unsigned int nSamples); + void callback(const float* input, float* output, unsigned int nSamples); - static wxArrayString getReadDevices(); - static wxArrayString getWriteDevices(); + static std::vector getReadDevices(); + static std::vector getWriteDevices(); private: - wxString m_readDevice; - wxString m_writeDevice; - unsigned int m_sampleRate; - unsigned int m_blockSize; - IAudioCallback* m_callback; - int m_id; - PaStream* m_stream; + std::string m_readDevice; + std::string m_writeDevice; + unsigned int m_sampleRate; + unsigned int m_blockSize; + IAudioCallback* m_callback; + int m_id; + PaStream* m_stream; bool convertNameToDevices(PaDeviceIndex& inDev, PaDeviceIndex& outDev); }; @@ -52,52 +81,63 @@ private: #else #include +#include -class CSoundCardReader : public wxThread { +// ALSA capture thread. entry() loops on snd_pcm_readi(), converts int16 samples +// to float, and delivers them to the IAudioCallback via readCallback(). +class CSoundCardReader { public: CSoundCardReader(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id); virtual ~CSoundCardReader(); - virtual void* Entry(); - - virtual void kill(); + void start(); + void kill(); + void join(); private: - snd_pcm_t* m_handle; - unsigned int m_blockSize; - unsigned int m_channels; - IAudioCallback* m_callback; - int m_id; - bool m_killed; - wxFloat32* m_buffer; - short* m_samples; + void entry(); + + snd_pcm_t* m_handle; + unsigned int m_blockSize; + unsigned int m_channels; + IAudioCallback* m_callback; + int m_id; + std::atomic m_killed; + float* m_buffer; + short* m_samples; + std::thread m_thread; }; -class CSoundCardWriter : public wxThread { +// ALSA playback thread. entry() calls writeCallback() to fill float samples, +// converts them to int16, and writes them via snd_pcm_writei(). +class CSoundCardWriter { public: CSoundCardWriter(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id); virtual ~CSoundCardWriter(); - virtual void* Entry(); - - virtual void kill(); + void start(); + void kill(); + void join(); - virtual bool isBusy() const; + bool isBusy() const; private: - snd_pcm_t* m_handle; - unsigned int m_blockSize; - unsigned int m_channels; - IAudioCallback* m_callback; - int m_id; - bool m_killed; - wxFloat32* m_buffer; - short* m_samples; + void entry(); + + snd_pcm_t* m_handle; + unsigned int m_blockSize; + unsigned int m_channels; + IAudioCallback* m_callback; + int m_id; + std::atomic m_killed; + float* m_buffer; + short* m_samples; + std::thread m_thread; }; class CSoundCardReaderWriter { public: - CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize); + CSoundCardReaderWriter(const std::string& readDevice, const std::string& writeDevice, unsigned int sampleRate, unsigned int blockSize); ~CSoundCardReaderWriter(); void setCallback(IAudioCallback* callback, int id); @@ -106,12 +146,12 @@ public: bool isWriterBusy() const; - static wxArrayString getReadDevices(); - static wxArrayString getWriteDevices(); + static std::vector getReadDevices(); + static std::vector getWriteDevices(); private: - wxString m_readDevice; - wxString m_writeDevice; + std::string m_readDevice; + std::string m_writeDevice; unsigned int m_sampleRate; unsigned int m_blockSize; IAudioCallback* m_callback; @@ -119,8 +159,8 @@ private: CSoundCardReader* m_reader; CSoundCardWriter* m_writer; - static wxArrayString m_readDevices; - static wxArrayString m_writeDevices; + static std::vector m_readDevices; + static std::vector m_writeDevices; }; #endif diff --git a/Common/SplitController.cpp b/Common/SplitController.cpp index bc730e1..e411dd0 100644 --- a/Common/SplitController.cpp +++ b/Common/SplitController.cpp @@ -17,22 +17,32 @@ */ #include "SplitController.h" +#include "Logger.h" + +#include +#include +#include +#include +#include +#include +#include +#include const unsigned int REGISTRATION_TIMEOUT = 200U; const unsigned int BUFFER_LENGTH = 200U; CAMBESlot::CAMBESlot(unsigned int rxCount) : -m_valid(NULL), +m_valid(nullptr), m_errors(999U), m_best(99U), -m_ambe(NULL), +m_ambe(nullptr), m_length(0U), -m_end(NULL), +m_end(nullptr), m_timer(1000U), m_rxCount(rxCount) { - wxASSERT(rxCount > 0U); + assert(rxCount > 0U); m_ambe = new unsigned char[DV_FRAME_MAX_LENGTH_BYTES]; m_valid = new bool[rxCount]; @@ -41,7 +51,7 @@ m_rxCount(rxCount) reset(); } -CAMBESlot::~CAMBESlot() +CAMBESlot::~CAMBESlot() { delete[] m_end; delete[] m_valid; @@ -67,7 +77,7 @@ bool CAMBESlot::isFirst() const return m_length == 0U; } -CSplitController::CSplitController(const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout) : +CSplitController::CSplitController(const std::string& localAddress, unsigned int localPort, const std::vector& transmitterNames, const std::vector& receiverNames, unsigned int timeout) : CModem(), m_handler(localAddress, localPort), m_transmitterNames(transmitterNames), @@ -75,12 +85,12 @@ m_receiverNames(receiverNames), m_timeout(timeout), m_txCount(0U), m_rxCount(0U), -m_txAddresses(NULL), -m_txPorts(NULL), -m_txTimers(NULL), -m_rxAddresses(NULL), -m_rxPorts(NULL), -m_rxTimers(NULL), +m_txAddresses(nullptr), +m_txPorts(nullptr), +m_txTimers(nullptr), +m_rxAddresses(nullptr), +m_rxPorts(nullptr), +m_rxTimers(nullptr), m_txData(1000U), m_outId(0x00U), m_outSeq(0U), @@ -88,21 +98,21 @@ m_endTimer(1000U, 1U), m_listening(true), m_inSeqNo(0x00U), m_outSeqNo(0x00U), -m_header(NULL), -m_id(NULL), -m_valid(NULL), -m_slots(NULL), +m_header(nullptr), +m_id(nullptr), +m_valid(nullptr), +m_slots(nullptr), m_headerSent(false), -m_packets(NULL), -m_best(NULL), -m_missed(NULL), +m_packets(nullptr), +m_best(nullptr), +m_missed(nullptr), m_silence(0U) { - m_txCount = transmitterNames.GetCount(); - m_rxCount = receiverNames.GetCount(); + m_txCount = (unsigned int)transmitterNames.size(); + m_rxCount = (unsigned int)receiverNames.size(); - wxASSERT(m_txCount > 0U); - wxASSERT(m_rxCount > 0U); + assert(m_txCount > 0U); + assert(m_rxCount > 0U); m_txAddresses = new in_addr[m_txCount]; m_txPorts = new unsigned int[m_txCount]; @@ -114,7 +124,7 @@ m_silence(0U) m_header = new unsigned char[RADIO_HEADER_LENGTH_BYTES]; - m_id = new wxUint16[m_rxCount]; + m_id = new uint16_t[m_rxCount]; m_valid = new bool[m_rxCount]; m_packets = new unsigned int[m_rxCount]; m_best = new unsigned int[m_rxCount]; @@ -172,44 +182,39 @@ bool CSplitController::start() if (!ret) return false; - Create(); - SetPriority(100U); - Run(); + m_thread = std::thread(&CSplitController::entry, this); return true; } -void* CSplitController::Entry() +void CSplitController::entry() { - wxLogMessage(wxT("Starting Split Controller thread")); - - wxStopWatch stopWatch; + wxLogMessage("Starting Split Controller thread"); while (!m_stopped) { - stopWatch.Start(); + auto loopStart = std::chrono::steady_clock::now(); transmit(); receive(); - Sleep(10UL); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); - unsigned int ms = stopWatch.Time(); + auto loopEnd = std::chrono::steady_clock::now(); + unsigned int ms = (unsigned int)std::chrono::duration_cast(loopEnd - loopStart).count(); timers(ms); } - wxLogMessage(wxT("Stopping Split Controller thread")); + wxLogMessage("Stopping Split Controller thread"); m_handler.close(); - - return NULL; } bool CSplitController::writeHeader(const CHeaderData& header) { bool ret = m_txData.hasSpace(RADIO_HEADER_LENGTH_BYTES); if (!ret) { - wxLogWarning(wxT("No space to write the header")); + wxLogWarning("No space to write the header"); return false; } @@ -221,27 +226,27 @@ bool CSplitController::writeHeader(const CHeaderData& header) buffer[1U] = header.getFlag2(); buffer[2U] = header.getFlag3(); - wxString rpt2 = header.getRptCall2(); - for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 3U] = rpt2.GetChar(i); + std::string rpt2 = header.getRptCall2(); + for (unsigned int i = 0U; i < rpt2.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 3U] = rpt2[i]; - wxString rpt1 = header.getRptCall1(); - for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 11U] = rpt1.GetChar(i); + std::string rpt1 = header.getRptCall1(); + for (unsigned int i = 0U; i < rpt1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 11U] = rpt1[i]; - wxString your = header.getYourCall(); - for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 19U] = your.GetChar(i); + std::string your = header.getYourCall(); + for (unsigned int i = 0U; i < your.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 19U] = your[i]; - wxString my1 = header.getMyCall1(); - for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++) - buffer[i + 27U] = my1.GetChar(i); + std::string my1 = header.getMyCall1(); + for (unsigned int i = 0U; i < my1.size() && i < LONG_CALLSIGN_LENGTH; i++) + buffer[i + 27U] = my1[i]; - wxString my2 = header.getMyCall2(); - for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) - buffer[i + 35U] = my2.GetChar(i); + std::string my2 = header.getMyCall2(); + for (unsigned int i = 0U; i < my2.size() && i < SHORT_CALLSIGN_LENGTH; i++) + buffer[i + 35U] = my2[i]; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_HEADER; @@ -257,11 +262,11 @@ bool CSplitController::writeData(const unsigned char* data, unsigned int length, { bool ret = m_txData.hasSpace(DV_FRAME_LENGTH_BYTES + 2U); if (!ret) { - wxLogWarning(wxT("No space to write data")); + wxLogWarning("No space to write data"); return false; } - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char buffer[2U]; buffer[0U] = end ? DSMTT_EOT : DSMTT_DATA; @@ -292,7 +297,7 @@ void CSplitController::transmit() unsigned char length = 0U; unsigned char buffer[RADIO_HEADER_LENGTH_BYTES]; { - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); m_txData.getData(&type, 1U); m_txData.getData(&length, 1U); @@ -300,7 +305,7 @@ void CSplitController::transmit() } if (type == DSMTT_HEADER) { - m_outId = (::rand() % 65535U) + 1U; + m_outId = (uint16_t)((::rand() % 65535U) + 1U); m_outSeq = 0U; m_tx = true; @@ -334,7 +339,7 @@ void CSplitController::receive() NETWORK_TYPE type = NETWORK_DATA; while (type != NETWORK_NONE) { - wxUint16 id; + uint16_t id; in_addr address; unsigned int port; @@ -359,74 +364,77 @@ void CSplitController::receive() } if (!found) { - wxString addr(::inet_ntoa(address), wxConvLocal); - wxLogError(wxT("Header received from unknown repeater - %s:%u"), addr.c_str(), port); + std::string addr(::inet_ntoa(address)); + wxLogError("Header received from unknown repeater - %s:%u", addr.c_str(), port); } } } else if (type == NETWORK_DATA) { unsigned char ambe[DV_FRAME_MAX_LENGTH_BYTES]; - wxUint8 seqNo; + uint8_t seqNo; unsigned int errors; unsigned int length = m_handler.readData(ambe, DV_FRAME_MAX_LENGTH_BYTES, seqNo, errors); for (unsigned int i = 0U; i < m_rxCount; i++) { if (address.s_addr == m_rxAddresses[i].s_addr && port == m_rxPorts[i]) { - processAMBE(i, id, ambe, length, seqNo, errors); + processAMBE(i, id, ambe, length, seqNo, (unsigned char)errors); m_rxTimers[i]->start(); break; } } } else if (type == NETWORK_REGISTER) { // These can be from transmitters and receivers - wxString name; + std::string name; m_handler.readRegister(name); - int n1 = m_receiverNames.Index(name); - int n2 = m_transmitterNames.Index(name); + auto it1 = std::find(m_receiverNames.begin(), m_receiverNames.end(), name); + auto it2 = std::find(m_transmitterNames.begin(), m_transmitterNames.end(), name); + + int n1 = (it1 != m_receiverNames.end()) ? (int)(it1 - m_receiverNames.begin()) : -1; + int n2 = (it2 != m_transmitterNames.end()) ? (int)(it2 - m_transmitterNames.begin()) : -1; - if (n1 != wxNOT_FOUND) { - wxASSERT(n1 < int(m_rxCount)); + if (n1 >= 0) { + assert(n1 < int(m_rxCount)); m_rxTimers[n1]->start(); if (m_rxAddresses[n1].s_addr != address.s_addr || m_rxPorts[n1] != port) { - wxString addr1(::inet_ntoa(m_rxAddresses[n1]), wxConvLocal); - wxString addr2(::inet_ntoa(address), wxConvLocal); + std::string addr1(::inet_ntoa(m_rxAddresses[n1])); + std::string addr2(::inet_ntoa(address)); if (m_rxPorts[n1] == 0U) - wxLogMessage(wxT("Registration of RX %d \"%s\" set to %s:%u"), n1 + 1, name.c_str(), addr2.c_str(), port); + wxLogMessage("Registration of RX %d \"%s\" set to %s:%u", n1 + 1, name.c_str(), addr2.c_str(), port); else - wxLogMessage(wxT("Registration of RX %d \"%s\" changed from %s:%u to %s:%u"), n1 + 1, name.c_str(), addr1.c_str(), m_rxPorts[n1], addr2.c_str(), port); + wxLogMessage("Registration of RX %d \"%s\" changed from %s:%u to %s:%u", n1 + 1, name.c_str(), addr1.c_str(), m_rxPorts[n1], addr2.c_str(), port); m_rxAddresses[n1].s_addr = address.s_addr; m_rxPorts[n1] = port; } } - if (n2 != wxNOT_FOUND) { - wxASSERT(n2 < int(m_txCount)); + if (n2 >= 0) { + assert(n2 < int(m_txCount)); m_txTimers[n2]->start(); if (m_txAddresses[n2].s_addr != address.s_addr || m_txPorts[n2] != port) { - wxString addr1(::inet_ntoa(m_txAddresses[n2]), wxConvLocal); - wxString addr2(::inet_ntoa(address), wxConvLocal); + std::string addr1(::inet_ntoa(m_txAddresses[n2])); + std::string addr2(::inet_ntoa(address)); if (m_txPorts[n2] == 0U) - wxLogMessage(wxT("Registration of TX %d \"%s\" set to %s:%u"), n2 + 1, name.c_str(), addr2.c_str(), port); + wxLogMessage("Registration of TX %d \"%s\" set to %s:%u", n2 + 1, name.c_str(), addr2.c_str(), port); else - wxLogMessage(wxT("Registration of TX %d \"%s\" changed from %s:%u to %s:%u"), n2 + 1, name.c_str(), addr1.c_str(), m_txPorts[n2], addr2.c_str(), port); + wxLogMessage("Registration of TX %d \"%s\" changed from %s:%u to %s:%u", n2 + 1, name.c_str(), addr1.c_str(), m_txPorts[n2], addr2.c_str(), port); m_txAddresses[n2].s_addr = address.s_addr; m_txPorts[n2] = port; } } - if (n1 == wxNOT_FOUND && n2 == wxNOT_FOUND) { - wxString addr(::inet_ntoa(address), wxConvLocal); - wxLogError(wxT("Registration of \"%s\" received from unknown repeater - %s:%u"), name.c_str(), addr.c_str(), port); + if (n1 < 0 && n2 < 0) { + std::string addr(::inet_ntoa(address)); + wxLogError("Registration of \"%s\" received from unknown repeater - %s:%u", name.c_str(), addr.c_str(), port); } } else { - wxString addr(::inet_ntoa(address), wxConvLocal); - wxLogError(wxT("Received invalid frame type %d from %s:%u"), int(type), addr.c_str(), port); + std::string addr(::inet_ntoa(address)); + wxLogError("Received invalid frame type %d from %s:%u", int(type), addr.c_str(), port); } } } @@ -437,7 +445,7 @@ void CSplitController::timers(unsigned int ms) if (m_endTimer.isRunning() && m_endTimer.hasExpired()) { printStats(); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); unsigned char data[2U]; data[0U] = DSMTT_EOT; data[1U] = 0U; @@ -453,7 +461,7 @@ void CSplitController::timers(unsigned int ms) for (unsigned int i = 0U; i < m_txCount; i++) { m_txTimers[i]->clock(ms); if (m_txTimers[i]->isRunning() && m_txTimers[i]->hasExpired()) { - wxLogWarning(wxT("TX %u registration has expired"), i + 1U); + wxLogWarning("TX %u registration has expired", i + 1U); m_txTimers[i]->stop(); m_txPorts[i] = 0U; } @@ -462,7 +470,7 @@ void CSplitController::timers(unsigned int ms) for (unsigned int i = 0U; i < m_rxCount; i++) { m_rxTimers[i]->clock(ms); if (m_rxTimers[i]->isRunning() && m_rxTimers[i]->hasExpired()) { - wxLogWarning(wxT("RX %u registration has expired"), i + 1U); + wxLogWarning("RX %u registration has expired", i + 1U); m_rxTimers[i]->stop(); m_rxPorts[i] = 0U; } @@ -476,8 +484,10 @@ void CSplitController::timers(unsigned int ms) for (unsigned int i = 0U; i < 21U; i++) m_slots[i]->m_timer.clock(ms); - // Check for expired timers - for (unsigned int i = 0U; i < 21U; i++) { + // Process up to 21 ready slots in sequence starting from m_outSeqNo. + // The loop counter limits iterations to prevent infinite spinning if + // m_outSeqNo wraps around. + for (unsigned int count = 0U; count < 21U; count++) { CAMBESlot* slot = m_slots[m_outSeqNo]; // Got to an unexpired timer @@ -488,7 +498,7 @@ void CSplitController::timers(unsigned int ms) if (isEnd(*slot)) { printStats(); - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); if (!m_headerSent) sendHeader(); @@ -504,11 +514,11 @@ void CSplitController::timers(unsigned int ms) return; } - // Is there any data? + // Is there any data? if (slot->m_length > 0U) { m_best[slot->m_best]++; - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); if (!m_headerSent) sendHeader(); @@ -523,7 +533,7 @@ void CSplitController::timers(unsigned int ms) m_silence++; // Send a silence frame to the repeater - wxMutexLocker locker(m_mutex); + std::lock_guard lock(m_mutex); if (!m_headerSent) sendHeader(); @@ -545,7 +555,7 @@ void CSplitController::timers(unsigned int ms) } } -void CSplitController::processHeader(unsigned int n, wxUint16 id, const unsigned char* header, unsigned int length) +void CSplitController::processHeader(unsigned int n, uint16_t id, const unsigned char* header, unsigned int length) { if (m_listening) { ::memcpy(m_header, header, RADIO_HEADER_LENGTH_BYTES - 2U); @@ -579,7 +589,14 @@ void CSplitController::processHeader(unsigned int n, wxUint16 id, const unsigned } } -void CSplitController::processAMBE(unsigned int n, wxUint16 id, const unsigned char* ambe, unsigned int length, wxUint8 seqNo, unsigned char errors) +// Processes one AMBE voice frame from receiver n. +// seqNo bit layout: +// bits [0..4] - sequence number within the super-frame (0-20) +// bit [6] - end-of-transmission flag +// Out-of-order detection: counts how many slots forward seqNo is relative to +// m_inSeqNo (wrapping at 21). Frames more than 18 slots ahead are discarded +// as duplicates or erroneous retransmissions. +void CSplitController::processAMBE(unsigned int n, uint16_t id, const unsigned char* ambe, unsigned int length, uint8_t seqNo, unsigned char errors) { if (m_listening) return; @@ -648,7 +665,7 @@ bool CSplitController::isEnd(const CAMBESlot& slot) const hasNoEnd = true; } - return hasEnd && !hasNoEnd; + return hasEnd && !hasNoEnd; } void CSplitController::sendHeader() @@ -673,75 +690,75 @@ void CSplitController::printStats() const total += m_best[i]; } - wxString temp; - wxString text = wxT("Packets: total/"); + char temp[64]; + std::string text = "Packets: total/"; for (unsigned int i = 0U; i < m_rxCount; i++) { if (m_rxPorts[i] > 0U) { - temp.Printf(wxT("rx%u/"), i + 1U); - text.Append(temp); + ::snprintf(temp, sizeof(temp), "rx%u/", i + 1U); + text += temp; } } - temp.Printf(wxT("silence %u/"), total); - text.Append(temp); + ::snprintf(temp, sizeof(temp), "silence %u/", total); + text += temp; for (unsigned int i = 0U; i < m_rxCount; i++) { if (m_rxPorts[i] > 0U) { - temp.Printf(wxT("%u/"), m_packets[i]); - text.Append(temp); + ::snprintf(temp, sizeof(temp), "%u/", m_packets[i]); + text += temp; } } - temp.Printf(wxT("%u, Proportion: "), m_silence); - text.Append(temp); + ::snprintf(temp, sizeof(temp), "%u, Proportion: ", m_silence); + text += temp; for (unsigned int i = 0U; i < m_rxCount; i++) { if (m_rxPorts[i] > 0U) { - temp.Printf(wxT("rx%u/"), i + 1U); - text.Append(temp); + ::snprintf(temp, sizeof(temp), "rx%u/", i + 1U); + text += temp; } } - text.Append(wxT("silence ")); + text += "silence "; for (unsigned int i = 0U; i < m_rxCount; i++) { if (m_rxPorts[i] > 0U) { - temp.Printf(wxT("%u%%%%/"), (100U * m_best[i]) / total); - text.Append(temp); + ::snprintf(temp, sizeof(temp), "%u%%/", (100U * m_best[i]) / total); + text += temp; } } - temp.Printf(wxT("%u%%%%, Missed: "), (100U * m_silence) / total); - text.Append(temp); + ::snprintf(temp, sizeof(temp), "%u%%, Missed: ", (100U * m_silence) / total); + text += temp; unsigned int n = 0U; for (unsigned int i = 0U; i < m_rxCount; i++) { if (m_rxPorts[i] > 0U) { if (n > 0U) - temp.Printf(wxT("/rx%u"), i + 1U); + ::snprintf(temp, sizeof(temp), "/rx%u", i + 1U); else - temp.Printf(wxT("rx%u"), i + 1U); + ::snprintf(temp, sizeof(temp), "rx%u", i + 1U); n++; - text.Append(temp); + text += temp; } } - text.Append(wxT(" ")); + text += " "; n = 0U; for (unsigned int i = 0U; i < m_rxCount; i++) { if (m_rxPorts[i] > 0U) { if (n > 0U) - temp.Printf(wxT("/%u%%%%"), (100U * m_missed[i]) / total); + ::snprintf(temp, sizeof(temp), "/%u%%", (100U * m_missed[i]) / total); else - temp.Printf(wxT("%u%%%%"), (100U * m_missed[i]) / total); + ::snprintf(temp, sizeof(temp), "%u%%", (100U * m_missed[i]) / total); n++; - text.Append(temp); + text += temp; } } - wxLogMessage(text); + wxLogMessage("%s", text.c_str()); } diff --git a/Common/SplitController.h b/Common/SplitController.h index e53b2ce..d8c1369 100644 --- a/Common/SplitController.h +++ b/Common/SplitController.h @@ -25,9 +25,26 @@ #include "Timer.h" #include "Modem.h" #include "Utils.h" +#include "StdCompat.h" -#include +#include +#include +#include +/* + * CAMBESlot - Holds one sequence-number slot's worth of AMBE data from all receivers. + * + * D-Star transmits 21 frames per super-frame (one data sync + 20 voice frames). + * CSplitController maintains 21 CAMBESlot objects, indexed by the D-Star sequence + * number. When AMBE data arrives from multiple receivers for the same slot, the + * copy with the fewest AMBE error bits (lowest m_errors) is kept as the best copy. + * + * m_valid[n] - true when receiver n has contributed data for this slot. + * m_end[n] - true when receiver n flagged end-of-transmission for this slot. + * m_best - index of the receiver whose data is currently stored in m_ambe. + * m_timer - deadline by which the slot must be forwarded to the repeater, + * even if not all receivers have responded. + */ class CAMBESlot { public: CAMBESlot(unsigned int rxCount); @@ -35,6 +52,7 @@ public: void reset(); + // Returns true when no receiver has deposited data yet (m_length == 0). bool isFirst() const; bool* m_valid; @@ -49,13 +67,48 @@ private: unsigned int m_rxCount; }; +/* + * CSplitController - Multi-receiver split-site repeater controller using UDP. + * + * Allows multiple geographically separated receive sites to feed a single + * repeater. Each receiver and transmitter connects to the controller via UDP + * using the CGatewayProtocolHandler (ircDDB gateway wire protocol). + * + * Network protocol: + * Receivers send NETWORK_HEADER and NETWORK_DATA packets, each containing + * the stream ID, sequence number, AMBE payload, and an error count. + * Transmitters receive NETWORK_HEADER and NETWORK_DATA forwarded by the + * controller's transmit() method. + * All remotes register by sending NETWORK_REGISTER with their name string; + * the controller maps names to IP:port and runs a REGISTRATION_TIMEOUT timer + * that expires if a remote goes silent. + * + * AMBE selection (best-copy diversity): + * For each D-Star sequence number slot (0-20), AMBE frames from all registered + * receivers are buffered in the corresponding CAMBESlot. The frame with the + * fewest AMBE error bits wins and is forwarded to the repeater when the slot + * timer expires. If no receiver provides data for a slot, a silence frame + * (NULL_FRAME_DATA_BYTES) is substituted. + * + * Resequencing: + * m_inSeqNo tracks the next expected sequence number from any receiver. + * m_outSeqNo tracks the next slot to be forwarded to the repeater. + * Out-of-order packets that are more than 18 slots ahead are discarded. + * + * Header forwarding (sendHeader()): + * Deferred until the first data slot is ready to be forwarded. This ensures + * the repeater receives header + data without a gap, and means m_headerSent + * guards against duplicate header injection. + * + * timeout: per-slot wait time in milliseconds added to each slot's timer on + * top of the expected frame arrival time. Increasing timeout improves + * diversity at the cost of added latency. + */ class CSplitController : public CModem { public: - CSplitController(const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout); + CSplitController(const std::string& localAddress, unsigned int localPort, const std::vector& transmitterNames, const std::vector& receiverNames, unsigned int timeout); virtual ~CSplitController(); - virtual void* Entry(); - virtual bool start(); virtual unsigned int getSpace(); @@ -65,9 +118,11 @@ public: virtual bool writeData(const unsigned char* data, unsigned int length, bool end); private: + void entry(); + CGatewayProtocolHandler m_handler; - wxArrayString m_transmitterNames; - wxArrayString m_receiverNames; + std::vector m_transmitterNames; + std::vector m_receiverNames; unsigned int m_timeout; unsigned int m_txCount; unsigned int m_rxCount; @@ -78,14 +133,14 @@ private: unsigned int* m_rxPorts; CTimer** m_rxTimers; CRingBuffer m_txData; - wxUint16 m_outId; - wxUint8 m_outSeq; + uint16_t m_outId; + uint8_t m_outSeq; CTimer m_endTimer; bool m_listening; - wxUint8 m_inSeqNo; - wxUint8 m_outSeqNo; + uint8_t m_inSeqNo; + uint8_t m_outSeqNo; unsigned char* m_header; - wxUint16* m_id; + uint16_t* m_id; bool* m_valid; CAMBESlot** m_slots; bool m_headerSent; @@ -94,13 +149,26 @@ private: unsigned int* m_missed; unsigned int m_silence; + // Drains m_txData and forwards header/data/EOT to all registered transmitters. void transmit(); + // Reads all pending UDP packets and dispatches them to processHeader/processAMBE + // or updates remote registrations. void receive(); + // Advances all timers by ms; expires slot timers and forwards data to m_rxData. void timers(unsigned int ms); - void processHeader(unsigned int n, wxUint16 id, const unsigned char* header, unsigned int length); - void processAMBE(unsigned int n, wxUint16 id, const unsigned char* ambe, unsigned int length, wxUint8 seqNo, unsigned char errors); + // Records a header from receiver n. First header starts the slot timers and + // initialises per-receiver tracking; subsequent headers from other receivers + // are validated by memcmp against the first. + void processHeader(unsigned int n, uint16_t id, const unsigned char* header, unsigned int length); + // Deposits an AMBE frame from receiver n into the appropriate sequence slot. + // Keeps the frame with the lowest error count; discards frames > 18 slots ahead. + void processAMBE(unsigned int n, uint16_t id, const unsigned char* ambe, unsigned int length, uint8_t seqNo, unsigned char errors); + // Returns true when all valid receivers have signalled end-of-transmission + // and none are still active in this slot. bool isEnd(const CAMBESlot& slot) const; + // Queues the buffered header into m_rxData; must be called with m_mutex held. void sendHeader(); + // Logs per-receiver packet counts, best-copy proportions, and missed frames. void printStats() const; }; diff --git a/DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.h b/Common/StdCompat.h similarity index 53% rename from DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.h rename to Common/StdCompat.h index da7bda3..809cb6a 100644 --- a/DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.h +++ b/Common/StdCompat.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010,2014 by Jonathan Naylor G4KLX + * Copyright (C) 2024 by the DStarRepeater contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,23 +16,28 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifndef DStarRepeaterConfigActiveHangSet_H -#define DStarRepeaterConfigActiveHangSet_H - -#include - -class CDStarRepeaterConfigActiveHangSet : public wxPanel { -public: - CDStarRepeaterConfigActiveHangSet(wxWindow* parent, int id, const wxString& title, unsigned int time); - virtual ~CDStarRepeaterConfigActiveHangSet(); - - virtual bool Validate(); +/* + * Compatibility header that provides the standard C++17 includes used + * throughout the codebase. This replaces the former wx/wx.h dependency so + * that non-GUI translation units compile without wxWidgets. Include this + * instead of individual standard headers wherever the full set is needed. + */ - virtual unsigned int getTime() const; +#ifndef StdCompat_H +#define StdCompat_H -private: - wxString m_title; - wxSlider* m_time; -}; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif diff --git a/Common/TCPReaderWriter.cpp b/Common/TCPReaderWriter.cpp index 7effc2c..06b9d7d 100644 --- a/Common/TCPReaderWriter.cpp +++ b/Common/TCPReaderWriter.cpp @@ -19,50 +19,52 @@ #include "TCPReaderWriter.h" #include "UDPReaderWriter.h" -#if !defined(__WINDOWS__) #include +#include +#include + +// Same Winsock RAII initialisation pattern as UDPReaderWriter. +#if defined(_WIN32) +namespace { +struct WinsockInit { + WinsockInit() { WSADATA d; WSAStartup(MAKEWORD(2, 2), &d); } + ~WinsockInit() { WSACleanup(); } +}; +static WinsockInit s_wsinit; +} #endif - -CTCPReaderWriter::CTCPReaderWriter(const wxString& address, unsigned int port, const wxString& localAddress) : +CTCPReaderWriter::CTCPReaderWriter(const std::string& address, unsigned int port, const std::string& localAddress) : m_address(address), m_port(port), m_localAddress(localAddress), +#if defined(_WIN32) +m_fd(INVALID_SOCKET) +#else m_fd(-1) -{ - wxASSERT(!address.IsEmpty()); - wxASSERT(port > 0U); - -#if defined(__WINDOWS__) - WSAData data; - int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); - if (wsaRet != 0) - wxLogError(wxT("Error from WSAStartup")); #endif +{ + assert(!address.empty()); + assert(port > 0U); } CTCPReaderWriter::CTCPReaderWriter() : m_address(), m_port(0U), m_localAddress(), +#if defined(_WIN32) +m_fd(INVALID_SOCKET) +#else m_fd(-1) -{ -#if defined(__WINDOWS__) - WSAData data; - int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); - if (wsaRet != 0) - wxLogError(wxT("Error from WSAStartup")); #endif +{ } CTCPReaderWriter::~CTCPReaderWriter() { -#if defined(__WINDOWS__) - ::WSACleanup(); -#endif } -bool CTCPReaderWriter::open(const wxString& address, unsigned int port, const wxString& localAddress) +bool CTCPReaderWriter::open(const std::string& address, unsigned int port, const std::string& localAddress) { m_address = address; m_port = port; @@ -73,43 +75,44 @@ bool CTCPReaderWriter::open(const wxString& address, unsigned int port, const wx bool CTCPReaderWriter::open() { +#if defined(_WIN32) + if (m_fd != INVALID_SOCKET) +#else if (m_fd != -1) +#endif return true; - if (m_address.IsEmpty() || m_port == 0U) + if (m_address.empty() || m_port == 0U) return false; m_fd = ::socket(PF_INET, SOCK_STREAM, 0); - if (m_fd < 0) { -#if defined(__WINDOWS__) - wxLogError(wxT("Cannot create the TCP socket, err=%d"), ::GetLastError()); +#if defined(_WIN32) + if (m_fd == INVALID_SOCKET) { + ::fprintf(stderr, "Cannot create the TCP socket, err=%d\n", WSAGetLastError()); #else - wxLogError(wxT("Cannot create the TCP socket, err=%d"), errno); + if (m_fd < 0) { + ::fprintf(stderr, "Cannot create the TCP socket, err=%d\n", errno); #endif return false; } - if (!m_localAddress.IsEmpty()) { + if (!m_localAddress.empty()) { sockaddr_in addr; ::memset(&addr, 0x00, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = 0U; -#if defined(__WINDOWS__) - addr.sin_addr.s_addr = ::inet_addr(m_localAddress.mb_str()); -#else - addr.sin_addr.s_addr = ::inet_addr(m_localAddress.mb_str()); -#endif + addr.sin_addr.s_addr = ::inet_addr(m_localAddress.c_str()); if (addr.sin_addr.s_addr == INADDR_NONE) { - wxLogError(wxT("The address is invalid - %s"), m_localAddress.c_str()); + ::fprintf(stderr, "The address is invalid - %s\n", m_localAddress.c_str()); close(); return false; } if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { -#if defined(__WINDOWS__) - wxLogError(wxT("Cannot bind the TCP address, err=%d"), ::GetLastError()); +#if defined(_WIN32) + ::fprintf(stderr, "Cannot bind the TCP address, err=%d\n", WSAGetLastError()); #else - wxLogError(wxT("Cannot bind the TCP address, err=%d"), errno); + ::fprintf(stderr, "Cannot bind the TCP address, err=%d\n", errno); #endif close(); return false; @@ -128,10 +131,10 @@ bool CTCPReaderWriter::open() } if (::connect(m_fd, (sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) { -#if defined(__WINDOWS__) - wxLogError(wxT("Cannot connect the TCP socket, err=%d"), ::GetLastError()); +#if defined(_WIN32) + ::fprintf(stderr, "Cannot connect the TCP socket, err=%d\n", WSAGetLastError()); #else - wxLogError(wxT("Cannot connect the TCP socket, err=%d"), errno); + ::fprintf(stderr, "Cannot connect the TCP socket, err=%d\n", errno); #endif close(); return false; @@ -139,10 +142,10 @@ bool CTCPReaderWriter::open() int noDelay = 1; if (::setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&noDelay, sizeof(noDelay)) == -1) { -#if defined(__WINDOWS__) - wxLogError(wxT("Cannot set the TCP socket option, err=%d"), ::GetLastError()); +#if defined(_WIN32) + ::fprintf(stderr, "Cannot set the TCP socket option, err=%d\n", WSAGetLastError()); #else - wxLogError(wxT("Cannot set the TCP socket option, err=%d"), errno); + ::fprintf(stderr, "Cannot set the TCP socket option, err=%d\n", errno); #endif close(); return false; @@ -153,48 +156,51 @@ bool CTCPReaderWriter::open() int CTCPReaderWriter::read(unsigned char* buffer, unsigned int length, unsigned int secs, unsigned int msecs) { - wxASSERT(buffer != NULL); - wxASSERT(length > 0U); - wxASSERT(m_fd != -1); + assert(buffer != nullptr); + assert(length > 0U); +#if defined(_WIN32) + assert(m_fd != INVALID_SOCKET); +#else + assert(m_fd != -1); +#endif // Check that the recv() won't block fd_set readFds; FD_ZERO(&readFds); -#if defined(__WINDOWS__) - FD_SET((unsigned int)m_fd, &readFds); -#else FD_SET(m_fd, &readFds); -#endif // Return after timeout timeval tv; tv.tv_sec = secs; tv.tv_usec = msecs * 1000; - int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); +#if defined(_WIN32) + int ret = ::select(0, &readFds, nullptr, nullptr, &tv); +#else + int ret = ::select(m_fd + 1, &readFds, nullptr, nullptr, &tv); +#endif if (ret < 0) { -#if defined(__WINDOWS__) - wxLogError(wxT("Error returned from TCP select, err=%d"), ::GetLastError()); +#if defined(_WIN32) + ::fprintf(stderr, "Error returned from TCP select, err=%d\n", WSAGetLastError()); #else - wxLogError(wxT("Error returned from TCP select, err=%d"), errno); + ::fprintf(stderr, "Error returned from TCP select, err=%d\n", errno); #endif return -1; } -#if defined(__WINDOWS__) - if (!FD_ISSET((unsigned int)m_fd, &readFds)) - return 0; -#else if (!FD_ISSET(m_fd, &readFds)) return 0; -#endif +#if defined(_WIN32) + int len = ::recv(m_fd, (char*)buffer, length, 0); +#else ssize_t len = ::recv(m_fd, (char*)buffer, length, 0); +#endif if (len < 0) { -#if defined(__WINDOWS__) - wxLogError(wxT("Error returned from recv, err=%d"), ::GetLastError()); +#if defined(_WIN32) + ::fprintf(stderr, "Error returned from recv, err=%d\n", WSAGetLastError()); #else - wxLogError(wxT("Error returned from recv, err=%d"), errno); + ::fprintf(stderr, "Error returned from recv, err=%d\n", errno); #endif return -1; } @@ -204,16 +210,22 @@ int CTCPReaderWriter::read(unsigned char* buffer, unsigned int length, unsigned bool CTCPReaderWriter::write(const unsigned char* buffer, unsigned int length) { - wxASSERT(buffer != NULL); - wxASSERT(length > 0U); - wxASSERT(m_fd != -1); + assert(buffer != nullptr); + assert(length > 0U); +#if defined(_WIN32) + assert(m_fd != INVALID_SOCKET); +#else + assert(m_fd != -1); +#endif +#if defined(_WIN32) + int ret = ::send(m_fd, (char *)buffer, length, 0); + if (ret != int(length)) { + ::fprintf(stderr, "Error returned from send, err=%d\n", WSAGetLastError()); +#else ssize_t ret = ::send(m_fd, (char *)buffer, length, 0); if (ret != ssize_t(length)) { -#if defined(__WINDOWS__) - wxLogError(wxT("Error returned from send, err=%d"), ::GetLastError()); -#else - wxLogError(wxT("Error returned from send, err=%d"), errno); + ::fprintf(stderr, "Error returned from send, err=%d\n", errno); #endif return false; } @@ -223,12 +235,15 @@ bool CTCPReaderWriter::write(const unsigned char* buffer, unsigned int length) void CTCPReaderWriter::close() { - if (m_fd != -1) { -#if defined(__WINDOWS__) +#if defined(_WIN32) + if (m_fd != INVALID_SOCKET) { ::closesocket(m_fd); + m_fd = INVALID_SOCKET; + } #else + if (m_fd != -1) { ::close(m_fd); -#endif m_fd = -1; } +#endif } diff --git a/Common/TCPReaderWriter.h b/Common/TCPReaderWriter.h index b67d53f..8280e8f 100644 --- a/Common/TCPReaderWriter.h +++ b/Common/TCPReaderWriter.h @@ -19,9 +19,14 @@ #ifndef TCPReaderWriter_H #define TCPReaderWriter_H -#include +#include "StdCompat.h" -#if !defined(__WINDOWS__) +// Platform socket headers. +#if defined(_WIN32) +#include +#include +#pragma comment(lib, "ws2_32.lib") +#else #include #include #include @@ -33,25 +38,45 @@ #include #endif +/* + * Thin wrapper around a blocking TCP client socket. + * + * open() resolves the remote hostname via CUDPReaderWriter::lookup(), creates + * a SOCK_STREAM socket, and calls connect(). TCP_NODELAY is set to avoid + * Nagle buffering delays on small control packets. + * + * read() uses select() with a caller-supplied timeout (seconds + milliseconds) + * so the caller can implement protocol-level timeouts without blocking forever. + * It returns the number of bytes received, 0 on timeout, or -1 on error. + * + * A default-constructed CTCPReaderWriter can be opened later by calling the + * two-argument open() overload, which also sets the address and port. + */ class CTCPReaderWriter { public: - CTCPReaderWriter(const wxString& address, unsigned int port, const wxString& localAddress = wxEmptyString); + CTCPReaderWriter(const std::string& address, unsigned int port, const std::string& localAddress = std::string()); CTCPReaderWriter(); ~CTCPReaderWriter(); - bool open(const wxString& address, unsigned int port, const wxString& localAddress = wxEmptyString); + bool open(const std::string& address, unsigned int port, const std::string& localAddress = std::string()); bool open(); + // Blocks until data arrives or the timeout (secs + msecs) expires. + // Returns bytes read, 0 on timeout, -1 on error. int read(unsigned char* buffer, unsigned int length, unsigned int secs, unsigned int msecs = 0U); bool write(const unsigned char* buffer, unsigned int length); void close(); private: - wxString m_address; + std::string m_address; unsigned short m_port; - wxString m_localAddress; + std::string m_localAddress; +#if defined(_WIN32) + SOCKET m_fd; +#else int m_fd; +#endif }; #endif diff --git a/Common/Timer.cpp b/Common/Timer.cpp index 39e2ff1..366af7e 100644 --- a/Common/Timer.cpp +++ b/Common/Timer.cpp @@ -18,19 +18,18 @@ #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) { - wxASSERT(ticksPerSec > 0U); + assert(ticksPerSec > 0U); if (secs > 0U || msecs > 0U) { - // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; - unsigned wxLongLong_t temp = (secs * wxULL(1000) + msecs) * m_ticksPerSec; - m_timeout = (unsigned int)(temp / wxULL(1000) + wxULL(1)); + unsigned long long temp = ((unsigned long long)secs * 1000ULL + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / 1000ULL + 1ULL); } } @@ -41,9 +40,8 @@ CTimer::~CTimer() void CTimer::setTimeout(unsigned int secs, unsigned int msecs) { if (secs > 0U || msecs > 0U) { - // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; - unsigned wxLongLong_t temp = (secs * wxULL(1000) + msecs) * m_ticksPerSec; - m_timeout = (unsigned int)(temp / wxULL(1000) + wxULL(1)); + unsigned long long temp = ((unsigned long long)secs * 1000ULL + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / 1000ULL + 1ULL); } else { m_timeout = 0U; m_timer = 0U; diff --git a/Common/Timer.h b/Common/Timer.h index 711242d..3a95efb 100644 --- a/Common/Timer.h +++ b/Common/Timer.h @@ -19,6 +19,20 @@ #ifndef Timer_H #define Timer_H +/* + * Tick-based countdown timer used for protocol timeouts. + * + * The timer counts upward in discrete ticks. A timeout expires when the + * internal counter reaches or exceeds the threshold derived from the + * configured duration. The caller drives the timer by calling clock() once + * per tick (or once per repeater loop iteration). + * + * ticksPerSec must match the rate at which clock() is called. For example, + * if clock() is called every millisecond, pass ticksPerSec=1000. + * + * A timer with a zero timeout is permanently stopped and will never expire. + * start() is a no-op if no timeout has been configured. + */ class CTimer { public: CTimer(unsigned int ticksPerSec = 1000, unsigned int secs = 0U, unsigned int msecs = 0U); @@ -74,6 +88,7 @@ public: return false; } + // Advance the timer by ticks steps; call once per repeater loop iteration. void clock(unsigned int ticks = 1U) { if (m_timer > 0U && m_timeout > 0U) @@ -82,8 +97,8 @@ public: private: unsigned int m_ticksPerSec; - unsigned int m_timeout; - unsigned int m_timer; + unsigned int m_timeout; // Expiry threshold in ticks (+1 to distinguish stopped from zero-elapsed). + unsigned int m_timer; // Current tick count; 0 means stopped. }; #endif diff --git a/Common/UDPReaderWriter.cpp b/Common/UDPReaderWriter.cpp index 5a18a8e..6457066 100644 --- a/Common/UDPReaderWriter.cpp +++ b/Common/UDPReaderWriter.cpp @@ -18,80 +18,72 @@ #include "UDPReaderWriter.h" -#if !defined(__WINDOWS__) #include +#include +#include + +// On Windows, Winsock must be initialised before any socket call and cleaned +// up at program exit. The file-static WinsockInit object handles this via +// its constructor/destructor, which run at program startup and shutdown. +#if defined(_WIN32) +namespace { +struct WinsockInit { + WinsockInit() { WSADATA d; WSAStartup(MAKEWORD(2, 2), &d); } + ~WinsockInit() { WSACleanup(); } +}; +static WinsockInit s_wsinit; +} #endif - -CUDPReaderWriter::CUDPReaderWriter(const wxString& address, unsigned int port) : +CUDPReaderWriter::CUDPReaderWriter(const std::string& address, unsigned int port) : m_address(address), m_port(port), m_addr(), +#if defined(_WIN32) +m_fd(INVALID_SOCKET) +#else m_fd(-1) -{ -#if defined(__WINDOWS__) - WSAData data; - int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); - if (wsaRet != 0) - wxLogError(wxT("Error from WSAStartup")); #endif +{ } CUDPReaderWriter::~CUDPReaderWriter() { -#if defined(__WINDOWS__) - ::WSACleanup(); -#endif } -in_addr CUDPReaderWriter::lookup(const wxString& hostname) +in_addr CUDPReaderWriter::lookup(const std::string& hostname) { in_addr addr; -#if defined(WIN32) - unsigned long address = ::inet_addr(hostname.mb_str()); - if (address != INADDR_NONE && address != INADDR_ANY) { - addr.s_addr = address; - return addr; - } - struct hostent* hp = ::gethostbyname(hostname.mb_str()); - if (hp != NULL) { - ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); - return addr; - } - - wxLogError(wxT("Cannot find address for host %s"), hostname.c_str()); - - addr.s_addr = INADDR_NONE; - return addr; -#else - in_addr_t address = ::inet_addr(hostname.mb_str()); + 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.mb_str()); - if (hp != NULL) { - ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + struct addrinfo hints{}, *res = nullptr; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + if (::getaddrinfo(hostname.c_str(), nullptr, &hints, &res) == 0 && res != nullptr) { + addr = reinterpret_cast(res->ai_addr)->sin_addr; + ::freeaddrinfo(res); return addr; } - wxLogError(wxT("Cannot find address for host %s"), hostname.c_str()); - + ::fprintf(stderr, "Cannot find address for host %s\n", hostname.c_str()); addr.s_addr = INADDR_NONE; return addr; -#endif } bool CUDPReaderWriter::open() { m_fd = ::socket(PF_INET, SOCK_DGRAM, 0); - if (m_fd < 0) { -#if defined(__WINDOWS__) - wxLogError(wxT("Cannot create the UDP socket, err: %lu"), ::GetLastError()); +#if defined(_WIN32) + if (m_fd == INVALID_SOCKET) { + ::fprintf(stderr, "Cannot create the UDP socket, err: %d\n", WSAGetLastError()); #else - wxLogError(wxT("Cannot create the UDP socket, err: %d"), errno); + if (m_fd < 0) { + ::fprintf(stderr, "Cannot create the UDP socket, err: %d\n", errno); #endif return false; } @@ -103,33 +95,29 @@ bool CUDPReaderWriter::open() addr.sin_port = htons(m_port); addr.sin_addr.s_addr = htonl(INADDR_ANY); - if (!m_address.IsEmpty()) { -#if defined(__WINDOWS__) - addr.sin_addr.s_addr = ::inet_addr(m_address.mb_str()); -#else - addr.sin_addr.s_addr = ::inet_addr(m_address.mb_str()); -#endif + if (!m_address.empty()) { + addr.sin_addr.s_addr = ::inet_addr(m_address.c_str()); if (addr.sin_addr.s_addr == INADDR_NONE) { - wxLogError(wxT("The address is invalid - %s"), m_address.c_str()); + ::fprintf(stderr, "The 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(__WINDOWS__) - wxLogError(wxT("Cannot set the UDP socket option (port: %u), err: %lu"), m_port, ::GetLastError()); +#if defined(_WIN32) + ::fprintf(stderr, "Cannot set the UDP socket option (port: %u), err: %d\n", m_port, WSAGetLastError()); #else - wxLogError(wxT("Cannot set the UDP socket option (port: %u), err: %d"), m_port, errno); + ::fprintf(stderr, "Cannot set the UDP socket option (port: %u), err: %d\n", m_port, errno); #endif return false; } if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { -#if defined(__WINDOWS__) - wxLogError(wxT("Cannot bind the UDP address (port: %u), err: %lu"), m_port, ::GetLastError()); +#if defined(_WIN32) + ::fprintf(stderr, "Cannot bind the UDP address (port: %u), err: %d\n", m_port, WSAGetLastError()); #else - wxLogError(wxT("Cannot bind the UDP address (port: %u), err: %d"), m_port, errno); + ::fprintf(stderr, "Cannot bind the UDP address (port: %u), err: %d\n", m_port, errno); #endif return false; } @@ -143,23 +131,23 @@ int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr& // Check that the readfrom() won't block fd_set readFds; FD_ZERO(&readFds); -#if defined(__WINDOWS__) - FD_SET((unsigned int)m_fd, &readFds); -#else FD_SET(m_fd, &readFds); -#endif // Return immediately timeval tv; tv.tv_sec = 0L; tv.tv_usec = 0L; - int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); +#if defined(_WIN32) + int ret = ::select(0, &readFds, nullptr, nullptr, &tv); +#else + int ret = ::select(m_fd + 1, &readFds, nullptr, nullptr, &tv); +#endif if (ret < 0) { -#if defined(__WINDOWS__) - wxLogError(wxT("Error returned from UDP select (port: %u), err: %lu"), m_port, ::GetLastError()); +#if defined(_WIN32) + ::fprintf(stderr, "Error returned from UDP select (port: %u), err: %d\n", m_port, WSAGetLastError()); #else - wxLogError(wxT("Error returned from UDP select (port: %u), err: %d"), m_port, errno); + ::fprintf(stderr, "Error returned from UDP select (port: %u), err: %d\n", m_port, errno); #endif return -1; } @@ -168,18 +156,18 @@ int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr& return 0; sockaddr_in addr; -#if defined(__WINDOWS__) - int size = sizeof(sockaddr_in); -#else socklen_t size = sizeof(sockaddr_in); -#endif +#if defined(_WIN32) + 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(__WINDOWS__) - wxLogError(wxT("Error returned from recvfrom (port: %u), err: %lu"), m_port, ::GetLastError()); +#if defined(_WIN32) + ::fprintf(stderr, "Error returned from recvfrom (port: %u), err: %d\n", m_port, WSAGetLastError()); #else - wxLogError(wxT("Error returned from recvfrom (port: %u), err: %d"), m_port, errno); + ::fprintf(stderr, "Error returned from recvfrom (port: %u), err: %d\n", m_port, errno); #endif return -1; } @@ -199,17 +187,25 @@ bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, c addr.sin_addr = address; addr.sin_port = htons(port); +#if defined(_WIN32) + 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(__WINDOWS__) - wxLogError(wxT("Error returned from sendto (port: %u), err: %lu"), m_port, ::GetLastError()); +#if defined(_WIN32) + ::fprintf(stderr, "Error returned from sendto (port: %u), err: %d\n", m_port, WSAGetLastError()); #else - wxLogError(wxT("Error returned from sendto (port: %u), err: %d"), m_port, errno); + ::fprintf(stderr, "Error returned from sendto (port: %u), err: %d\n", m_port, errno); #endif return false; } +#if defined(_WIN32) + if (ret != int(length)) +#else if (ret != ssize_t(length)) +#endif return false; return true; @@ -217,7 +213,7 @@ bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, c void CUDPReaderWriter::close() { -#if defined(__WINDOWS__) +#if defined(_WIN32) ::closesocket(m_fd); #else ::close(m_fd); diff --git a/Common/UDPReaderWriter.h b/Common/UDPReaderWriter.h index 4167662..3fc762a 100644 --- a/Common/UDPReaderWriter.h +++ b/Common/UDPReaderWriter.h @@ -19,9 +19,14 @@ #ifndef UDPReaderWriter_H #define UDPReaderWriter_H -#include +#include "StdCompat.h" -#if !defined(__WINDOWS__) +// Platform socket headers — Winsock on Windows, POSIX on everything else. +#if defined(_WIN32) +#include +#include +#pragma comment(lib, "ws2_32.lib") +#else #include #include #include @@ -32,15 +37,28 @@ #include #endif +/* + * Thin wrapper around a single UDP socket. + * + * open() creates and binds a SOCK_DGRAM socket to the configured address and + * port. read() is non-blocking (uses select() with a zero timeout); write() + * sends to an explicit destination address supplied by the caller. + * + * The static lookup() method resolves a hostname or dotted-decimal string to + * an in_addr, returning INADDR_NONE on failure. + */ class CUDPReaderWriter { public: - CUDPReaderWriter(const wxString& address, unsigned int port); + CUDPReaderWriter(const std::string& address, unsigned int port); ~CUDPReaderWriter(); - static in_addr lookup(const wxString& hostName); + // Resolves hostname or dotted-decimal IP string to in_addr. + // Tries inet_addr() first; falls back to getaddrinfo() for hostnames. + static in_addr lookup(const std::string& hostName); bool open(); + // Returns the number of bytes received, 0 if no datagram was ready, or -1 on error. 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); @@ -49,10 +67,14 @@ public: unsigned int getPort() const; private: - wxString m_address; + std::string m_address; unsigned short m_port; in_addr m_addr; +#if defined(_WIN32) + SOCKET m_fd; +#else int m_fd; +#endif }; #endif diff --git a/Common/UDRCController.cpp b/Common/UDRCController.cpp index 61a0862..7577a9c 100644 --- a/Common/UDRCController.cpp +++ b/Common/UDRCController.cpp @@ -8,6 +8,12 @@ #include #include #include +#include +#if !defined(_WIN32) +#include +#endif +#include +#include #include "UDRCController.h" @@ -52,12 +58,12 @@ static int getNWDRProductID() { long product_id = 0; if((proc_file = open(vendor_string_path, O_RDONLY|O_NONBLOCK)) == -1) { - wxLogError("Open %s: %s\n", vendor_string_path, strerror(errno)); + ::fprintf(stderr, "Open %s: %s\n", vendor_string_path, strerror(errno)); } read_size = read(proc_file, buffer, sizeof(buffer)); if(read_size < 1) { - wxLogError("Read %s: %s\n", vendor_string_path, strerror(errno)); + ::fprintf(stderr, "Read %s: %s\n", vendor_string_path, strerror(errno)); close(proc_file); return -1; } @@ -65,26 +71,26 @@ static int getNWDRProductID() { close(proc_file); if(strncmp(nwdr_vendor_string, buffer, sizeof(nwdr_vendor_string))) { - wxLogError("HAT is not a NW Digital Radio Product: %.255s\n", buffer); + ::fprintf(stderr, "HAT is not a NW Digital Radio Product: %.255s\n", buffer); return -1; } if((proc_file = open(product_id_string_path, O_RDONLY|O_NONBLOCK)) == -1) { - wxLogError("Open %s: %s\n", product_id_string_path, strerror(errno)); + ::fprintf(stderr, "Open %s: %s\n", product_id_string_path, strerror(errno)); } read_size = read(proc_file, buffer, sizeof(buffer)); if(read_size < 1) { - wxLogError("Read %s: %s\n", product_id_string_path, strerror(errno)); + ::fprintf(stderr, "Read %s: %s\n", product_id_string_path, strerror(errno)); close(proc_file); return -1; } close(proc_file); - product_id = strtol(buffer, NULL, 16); + product_id = strtol(buffer, nullptr, 16); if(product_id == EINVAL || product_id == ERANGE) { - wxLogError("Invalid value for product id: %.255s\n", buffer); + ::fprintf(stderr, "Invalid value for product id: %.255s\n", buffer); return -1; } @@ -93,7 +99,7 @@ static int getNWDRProductID() { // XXX This is ugly, we should have a better parent class CUDRCController::CUDRCController(enum repeater_modes mode) : -CExternalController(NULL, false), +CExternalController(nullptr, false), m_mode(mode), m_pttPin(PTT_PIN) { @@ -129,7 +135,7 @@ void CUDRCController::switchMode(enum repeater_modes mode) { bool CUDRCController::open() { if(::wiringPiSetupGpio() != 0) { - wxLogError("Unable to initialize the wiringPi library"); + ::fprintf(stderr, "Unable to initialize the wiringPi library\n"); return false; } @@ -163,7 +169,7 @@ bool CUDRCController::open() case -1: return false; default: - wxLogError("Unknown NW Digital Radio Product"); + ::fprintf(stderr, "Unknown NW Digital Radio Product\n"); return false; } @@ -218,4 +224,3 @@ void CUDRCController::close() } #endif - diff --git a/Common/URIUSBController.cpp b/Common/URIUSBController.cpp index 893c545..59abadb 100644 --- a/Common/URIUSBController.cpp +++ b/Common/URIUSBController.cpp @@ -13,6 +13,11 @@ #include "URIUSBController.h" +#include +#include + +#if !defined(_WIN32) + const unsigned int C108_VENDOR_ID = 0x0D8CU; const unsigned int C108_PRODUCT_ID = 0x000CU; const unsigned int C108AH_PRODUCT_ID = 0x013CU; @@ -34,194 +39,6 @@ const char HID_IR0_PM = 0x04; // Playback-Mute, Battery const char HID_IR0_VD = 0x02; // Volume-Down, COR_DET const char HID_IR0_VU = 0x01; // Volume-Up, CTCSS_DET -#if defined(__WINDOWS__) - -#include -#include - -const ULONG USB_BUFSIZE = 5UL; - -CURIUSBController::CURIUSBController(unsigned int address, bool checkInput) : -m_address(address), -m_checkInput(checkInput), -m_outp1(false), -m_outp3(false), -m_outp5(false), -m_outp6(false), -m_handle(INVALID_HANDLE_VALUE) -{ -} - -CURIUSBController::~CURIUSBController() -{ -} - -bool CURIUSBController::open() -{ - wxASSERT(m_handle == INVALID_HANDLE_VALUE); - - GUID guid; - ::HidD_GetHidGuid(&guid); - - HDEVINFO devInfo = ::SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); - if (devInfo == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError()); - return false; - } - - SP_DEVICE_INTERFACE_DATA devInfoData; - devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - - unsigned int count = 0U; - for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guid, index, &devInfoData); index++) { - // Find the required length of the device structure - DWORD length; - ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL); - - PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length)); - detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - // Get the detailed data into the newly allocated device structure - DWORD required; - BOOL res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL); - if (!res) { - wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - // Get the handle for getting the attributes - HANDLE handle = ::CreateFile(detailData->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (handle == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from CreateFile: err=%u"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - HIDD_ATTRIBUTES attributes; - attributes.Size = sizeof(HIDD_ATTRIBUTES); - res = ::HidD_GetAttributes(handle, &attributes); - if (!res) { - wxLogError(wxT("Error from HidD_GetAttributes: err=%u"), ::GetLastError()); - ::CloseHandle(handle); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - ::CloseHandle(handle); - - // Is this a CM108 or equivalent? - if (attributes.VendorID == C108_VENDOR_ID && - (attributes.ProductID == C108_PRODUCT_ID || - attributes.ProductID == C108AH_PRODUCT_ID || - attributes.ProductID == C119_PRODUCT_ID || - attributes.ProductID == C119A_PRODUCT_ID)) { - count++; - - // Is this the right device? - if (count == m_address) { - m_handle = ::CreateFile(detailData->DevicePath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (m_handle == INVALID_HANDLE_VALUE) { - wxLogError(wxT("Error from CreateFile: err=%u"), ::GetLastError()); - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - return false; - } - - ::SetupDiDestroyDeviceInfoList(devInfo); - ::free(detailData); - - setDigitalOutputs(false, false, false, false, false, false, false, false); - - return true; - } - } - - ::free(detailData); - } - - ::SetupDiDestroyDeviceInfoList(devInfo); - - return false; -} - -void CURIUSBController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) -{ - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - - if (!m_checkInput) { - inp1 = inp2 = inp3 = inp4 = inp5 = false; - return; - } - - char buffer[USB_BUFSIZE]; - buffer[0] = REPORT_ID; - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - BOOL res = ::HidD_GetInputReport(m_handle, buffer, USB_BUFSIZE); - if (!res) { - wxLogError(wxT("Error from HidD_GetInputReport: err=%u"), ::GetLastError()); - return; - } - - inp3 = false; - - inp5 = (buffer[1] & HID_IR0_RM) == HID_IR0_RM; // Disable - inp4 = (buffer[1] & HID_IR0_PM) == HID_IR0_PM; // Battery - inp2 = (buffer[1] & HID_IR0_VU) == HID_IR0_VU; // Squelch 2 - inp1 = (buffer[1] & HID_IR0_VD) == HID_IR0_VD; // Squelch 1 -} - -void CURIUSBController::setDigitalOutputs(bool outp1, bool, bool outp3, bool outp4, bool outp5, bool outp6, bool, bool) -{ - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - - if (outp1 == m_outp1 && outp3 == m_outp3 && outp5 == m_outp5 && outp6 == m_outp6) - return; - - char buffer[USB_BUFSIZE]; - buffer[0] = REPORT_ID; - buffer[1] = HID_OR0; - buffer[2] = HID_OR1; - buffer[3] = HID_OR2; - buffer[4] = HID_OR3; - - if (outp1) // Transmit - buffer[2] |= HID_OR1_GPIO3; - if (outp3) // Heartbeat - buffer[2] |= HID_OR1_GPIO1; - if (outp4) // Active - buffer[2] |= HID_OR1_GPIO2; - if (outp5) // Output 1 - buffer[2] |= HID_OR1_GPIO4; - - BOOL res = ::HidD_SetOutputReport(m_handle, buffer, USB_BUFSIZE); - if (!res) - wxLogError(wxT("Error from HidD_SetOutputReport: err=%u"), ::GetLastError()); - - m_outp1 = outp1; - m_outp3 = outp3; - m_outp5 = outp5; - m_outp6 = outp6; -} - -void CURIUSBController::close() -{ - wxASSERT(m_handle != INVALID_HANDLE_VALUE); - - setDigitalOutputs(false, false, false, false, false, false, false, false); - - ::CloseHandle(m_handle); - m_handle = INVALID_HANDLE_VALUE; -} - -#else - const unsigned int C108_HID_INTERFACE = 3U; const unsigned int HID_REPORT_GET = 0x01U; @@ -241,28 +58,28 @@ m_outp1(false), m_outp3(false), m_outp5(false), m_outp6(false), -m_context(NULL), -m_handle(NULL) +m_context(nullptr), +m_handle(nullptr) { ::libusb_init(&m_context); } CURIUSBController::~CURIUSBController() { - wxASSERT(m_context != NULL); + assert(m_context != nullptr); ::libusb_exit(m_context); } bool CURIUSBController::open() { - wxASSERT(m_context != NULL); - wxASSERT(m_handle == NULL); + assert(m_context != nullptr); + assert(m_handle == nullptr); libusb_device** list; - ssize_t cnt = ::libusb_get_device_list(NULL, &list); + ssize_t cnt = ::libusb_get_device_list(nullptr, &list); if (cnt <= 0) { - wxLogError(wxT("Cannot find any USB devices!")); + ::fprintf(stderr, "Cannot find any USB devices!\n"); return false; } @@ -290,13 +107,13 @@ bool CURIUSBController::open() } if (err != 0) { - wxLogError(wxT("libusb_open failed, err=%d"), err); + ::fprintf(stderr, "libusb_open failed, err=%d\n", err); ::libusb_free_device_list(list, 1); return false; } - if (m_handle == NULL) { - wxLogError(wxT("Could not find a suitable CM108 based device")); + if (m_handle == nullptr) { + ::fprintf(stderr, "Could not find a suitable CM108 based device\n"); ::libusb_free_device_list(list, 1); return false; } @@ -307,17 +124,17 @@ bool CURIUSBController::open() if (res != 0) { res = ::libusb_detach_kernel_driver(m_handle, C108_HID_INTERFACE); if (res != 0) { - wxLogError(wxT("libusb_detach_kernel_driver failed, err=%d"), res); + ::fprintf(stderr, "libusb_detach_kernel_driver failed, err=%d\n", res); ::libusb_close(m_handle); - m_handle = NULL; + m_handle = nullptr; return false; } res = ::libusb_claim_interface(m_handle, C108_HID_INTERFACE); if (res != 0) { - wxLogError(wxT("libusb_claim_interface failed, err=%d"), res); + ::fprintf(stderr, "libusb_claim_interface failed, err=%d\n", res); ::libusb_close(m_handle); - m_handle = NULL; + m_handle = nullptr; return false; } } @@ -329,7 +146,7 @@ bool CURIUSBController::open() void CURIUSBController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) { - wxASSERT(m_handle != NULL); + assert(m_handle != nullptr); if (!m_checkInput) { inp1 = inp2 = inp3 = inp4 = inp5 = false; @@ -343,7 +160,7 @@ void CURIUSBController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, boo buffer[3] = 0x00; int res = ::libusb_control_transfer(m_handle, LIBUSB_ENDPOINT_IN + LIBUSB_REQUEST_TYPE_CLASS + LIBUSB_RECIPIENT_INTERFACE, HID_REPORT_GET, REPORT_ID + (HID_RT_INPUT << 8), C108_HID_INTERFACE, buffer, USB_BUFSIZE, USB_TIMEOUT); if (res < 0) { - wxLogError(wxT("Error from libusb_control_transfer: err=%d"), res); + ::fprintf(stderr, "Error from libusb_control_transfer: err=%d\n", res); return; } @@ -360,7 +177,7 @@ void CURIUSBController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, boo void CURIUSBController::setDigitalOutputs(bool outp1, bool, bool outp3, bool outp4, bool outp5, bool outp6, bool, bool) { - wxASSERT(m_handle != NULL); + assert(m_handle != nullptr); if (outp1 == m_outp1 && outp3 == m_outp3 && outp5 == m_outp5 && outp6 == m_outp6) return; @@ -382,7 +199,7 @@ void CURIUSBController::setDigitalOutputs(bool outp1, bool, bool outp3, bool out int res = ::libusb_control_transfer(m_handle, LIBUSB_ENDPOINT_OUT + LIBUSB_REQUEST_TYPE_CLASS + LIBUSB_RECIPIENT_INTERFACE, HID_REPORT_SET, REPORT_ID + (HID_RT_OUTPUT << 8), C108_HID_INTERFACE, buffer, USB_BUFSIZE, USB_TIMEOUT); if (res < 0) { - wxLogError(wxT("Error from libusb_control_transfer: err=%d"), res); + ::fprintf(stderr, "Error from libusb_control_transfer: err=%d\n", res); return; } @@ -397,15 +214,47 @@ void CURIUSBController::setDigitalOutputs(bool outp1, bool, bool outp3, bool out void CURIUSBController::close() { - wxASSERT(m_handle != NULL); + assert(m_handle != nullptr); setDigitalOutputs(false, false, false, false, false, false, false, false); ::libusb_release_interface(m_handle, C108_HID_INTERFACE); ::libusb_close(m_handle); - m_handle = NULL; + m_handle = nullptr; } -#endif +#else + +// Windows stub implementation + +CURIUSBController::CURIUSBController(unsigned int address, bool checkInput) : +m_address(address), +m_checkInput(checkInput) +{ +} + +CURIUSBController::~CURIUSBController() +{ +} +bool CURIUSBController::open() +{ + ::fprintf(stderr, "K8055 not supported on Windows without Velleman DLL\n"); + return false; +} + +void CURIUSBController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) +{ + inp1 = inp2 = inp3 = inp4 = inp5 = false; +} + +void CURIUSBController::setDigitalOutputs(bool, bool, bool, bool, bool, bool, bool, bool) +{ +} + +void CURIUSBController::close() +{ +} + +#endif diff --git a/Common/URIUSBController.h b/Common/URIUSBController.h index 52fb2d1..ac59c47 100644 --- a/Common/URIUSBController.h +++ b/Common/URIUSBController.h @@ -16,13 +16,9 @@ #include "HardwareController.h" -#include +#if !defined(_WIN32) -#if defined(__WINDOWS__) -#include -#else #include -#endif class CURIUSBController : public IHardwareController { public: @@ -44,12 +40,33 @@ private: bool m_outp3; bool m_outp5; bool m_outp6; -#if defined(__WINDOWS__) - HANDLE m_handle; -#else libusb_context* m_context; libusb_device_handle* m_handle; -#endif }; +#else + +// Windows stub — URI USB (CM108-based) controller requires libusb which is not +// available in this build configuration. open() will log an error and return false. + +class CURIUSBController : public IHardwareController { +public: + CURIUSBController(unsigned int address, bool checkInput); + virtual ~CURIUSBController(); + + virtual bool open(); + + virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5); + + virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8); + + virtual void close(); + +private: + unsigned int m_address; + bool m_checkInput; +}; + +#endif + #endif diff --git a/Common/Utils.cpp b/Common/Utils.cpp index b89758d..e66db97 100644 --- a/Common/Utils.cpp +++ b/Common/Utils.cpp @@ -13,43 +13,48 @@ #include "Utils.h" -void CUtils::dump(const wxChar* title, const unsigned char* data, unsigned int length) +#include +#include +#include +#include + +void CUtils::dump(const char* title, const unsigned char* data, unsigned int length) { - wxASSERT(title != NULL); - wxASSERT(data != NULL); + assert(title != nullptr); + assert(data != nullptr); - wxLogMessage(wxT("%s"), title); + ::fprintf(stdout, "%s\n", title); unsigned int offset = 0U; while (length > 0U) { - wxString output; + std::string output; unsigned int bytes = (length > 16U) ? 16U : length; + char temp[8]; for (unsigned i = 0U; i < bytes; i++) { - wxString temp; - temp.Printf(wxT("%02X "), data[offset + i]); + ::snprintf(temp, sizeof(temp), "%02X ", data[offset + i]); output += temp; } for (unsigned int i = bytes; i < 16U; i++) - output += wxT(" "); + output += " "; - output += wxT(" *"); + output += " *"; for (unsigned i = 0U; i < bytes; i++) { unsigned char c = data[offset + i]; if (::isprint(c)) - output += wxChar(c); + output += char(c); else - output += wxT('.'); + output += '.'; } - output += wxT('*'); + output += '*'; - wxLogMessage(wxT("%04X: %s"), offset, output.c_str()); + ::fprintf(stdout, "%04X: %s\n", offset, output.c_str()); offset += 16U; diff --git a/Common/Utils.h b/Common/Utils.h index 21d6b17..0a445c8 100644 --- a/Common/Utils.h +++ b/Common/Utils.h @@ -14,7 +14,7 @@ #ifndef Utils_H #define Utils_H -#include +#include "StdCompat.h" enum TRISTATE { STATE_FALSE, @@ -22,9 +22,17 @@ enum TRISTATE { STATE_UNKNOWN }; +/* + * General utility class. + * + * Currently contains only dump(), a hex-dump helper used throughout the + * codebase to print raw protocol bytes to stdout for debugging. Output + * format is classic hex+ASCII: 16 bytes per line, address on the left, + * printable characters on the right. + */ class CUtils { public: - static void dump(const wxChar* title, const unsigned char* data, unsigned int length); + static void dump(const char* title, const unsigned char* data, unsigned int length); private: }; diff --git a/Common/Version.h b/Common/Version.h index e452982..767c264 100644 --- a/Common/Version.h +++ b/Common/Version.h @@ -16,17 +16,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +// Application vendor and build-date version string, reported in the GUI title +// bar and log output at startup. + #ifndef Version_H #define Version_H -#include - -const wxString VENDOR_NAME = wxT("G4KLX"); +#include -#if defined(__WXDEBUG__) -const wxString VERSION = wxT("20180911 - DEBUG"); -#else -const wxString VERSION = wxT("20180911"); -#endif +inline const std::string VENDOR_NAME = "G4KLX"; +inline const std::string VERSION = "20260319"; #endif diff --git a/DStarRepeater.sln b/DStarRepeater.sln deleted file mode 100644 index 5e1ac62..0000000 --- a/DStarRepeater.sln +++ /dev/null @@ -1,61 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2000 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DStarRepeater", "DStarRepeater\DStarRepeater.vcxproj", "{E3458953-FC3D-47AF-A245-A1A87DC2BC7E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DStarRepeaterConfig", "DStarRepeaterConfig\DStarRepeaterConfig.vcxproj", "{FBAE8201-11CC-4256-BF39-A860B9DF963C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Common\Common.vcxproj", "{3753EF20-2330-415E-B933-2560A474498B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GUICommon", "GUICommon\GUICommon.vcxproj", "{2C548AAB-A9F7-496F-BCF5-6185BDA304D2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Debug|Win32.ActiveCfg = Debug|Win32 - {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Debug|Win32.Build.0 = Debug|Win32 - {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Debug|x64.ActiveCfg = Debug|x64 - {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Debug|x64.Build.0 = Debug|x64 - {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Release|Win32.ActiveCfg = Release|Win32 - {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Release|Win32.Build.0 = Release|Win32 - {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Release|x64.ActiveCfg = Release|x64 - {E3458953-FC3D-47AF-A245-A1A87DC2BC7E}.Release|x64.Build.0 = Release|x64 - {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Debug|Win32.ActiveCfg = Debug|Win32 - {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Debug|Win32.Build.0 = Debug|Win32 - {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Debug|x64.ActiveCfg = Debug|x64 - {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Debug|x64.Build.0 = Debug|x64 - {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Release|Win32.ActiveCfg = Release|Win32 - {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Release|Win32.Build.0 = Release|Win32 - {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Release|x64.ActiveCfg = Release|x64 - {FBAE8201-11CC-4256-BF39-A860B9DF963C}.Release|x64.Build.0 = Release|x64 - {3753EF20-2330-415E-B933-2560A474498B}.Debug|Win32.ActiveCfg = Debug|Win32 - {3753EF20-2330-415E-B933-2560A474498B}.Debug|Win32.Build.0 = Debug|Win32 - {3753EF20-2330-415E-B933-2560A474498B}.Debug|x64.ActiveCfg = Debug|x64 - {3753EF20-2330-415E-B933-2560A474498B}.Debug|x64.Build.0 = Debug|x64 - {3753EF20-2330-415E-B933-2560A474498B}.Release|Win32.ActiveCfg = Release|Win32 - {3753EF20-2330-415E-B933-2560A474498B}.Release|Win32.Build.0 = Release|Win32 - {3753EF20-2330-415E-B933-2560A474498B}.Release|x64.ActiveCfg = Release|x64 - {3753EF20-2330-415E-B933-2560A474498B}.Release|x64.Build.0 = Release|x64 - {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Debug|Win32.ActiveCfg = Debug|Win32 - {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Debug|Win32.Build.0 = Debug|Win32 - {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Debug|x64.ActiveCfg = Debug|x64 - {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Debug|x64.Build.0 = Debug|x64 - {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Release|Win32.ActiveCfg = Release|Win32 - {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Release|Win32.Build.0 = Release|Win32 - {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Release|x64.ActiveCfg = Release|x64 - {2C548AAB-A9F7-496F-BCF5-6185BDA304D2}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {F4EB6A2D-4233-46ED-9DEC-6A55E5765F30} - EndGlobalSection -EndGlobal diff --git a/DStarRepeater/DStarRepeater.vcxproj b/DStarRepeater/DStarRepeater.vcxproj deleted file mode 100644 index 3b2c43c..0000000 --- a/DStarRepeater/DStarRepeater.vcxproj +++ /dev/null @@ -1,204 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {E3458953-FC3D-47AF-A245-A1A87DC2BC7E} - DStarRepeater - Win32Proj - 10.0 - - - - Application - v142 - Unicode - true - - - Application - v142 - Unicode - true - - - Application - v142 - Unicode - - - Application - v142 - Unicode - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>14.0.24720.0 - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - true - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswud - - - true - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswud - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswu - - - false - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswu - - - - Disabled - $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(SolutionDir)Common;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - - Level4 - EditAndContinue - - - winusb.lib;wsock32.lib;setupapi.lib;hid.lib;portaudio_x86.lib;%(AdditionalDependencies) - $(SolutionDir)..\portaudio\build\msvc\Win32\Debug;$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) - true - Windows - MachineX86 - - - - - Disabled - $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(SolutionDir)Common;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - ProgramDatabase - - - winusb.lib;wsock32.lib;setupapi.lib;hid.lib;portaudio_x64.lib;%(AdditionalDependencies) - $(SolutionDir)..\portaudio\build\msvc\x64\Debug;$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) - true - Windows - - - - - MaxSpeed - true - $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(SolutionDir)Common;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - Level3 - ProgramDatabase - - - winusb.lib;wsock32.lib;setupapi.lib;hid.lib;portaudio_x86.lib;%(AdditionalDependencies) - $(SolutionDir)..\portaudio\build\msvc\Win32\Release;$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) - true - Windows - true - true - MachineX86 - - - - - MaxSpeed - true - $(SolutionDir)..\WinUSB\include;$(SolutionDir)..\portaudio\include;$(WXWIN)\include\msvc;$(SolutionDir)Common;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - - - winusb.lib;wsock32.lib;setupapi.lib;hid.lib;portaudio_x64.lib;%(AdditionalDependencies) - $(SolutionDir)..\portaudio\build\msvc\x64\Release;$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) - true - Windows - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - {3753ef20-2330-415e-b933-2560a474498b} - false - - - - - - \ No newline at end of file diff --git a/DStarRepeater/DStarRepeater.vcxproj.filters b/DStarRepeater/DStarRepeater.vcxproj.filters deleted file mode 100644 index 09f9945..0000000 --- a/DStarRepeater/DStarRepeater.vcxproj.filters +++ /dev/null @@ -1,74 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/DStarRepeater/DStarRepeaterApp.cpp b/DStarRepeater/DStarRepeaterApp.cpp index beb0d92..4e784d3 100644 --- a/DStarRepeater/DStarRepeaterApp.cpp +++ b/DStarRepeater/DStarRepeaterApp.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2011-2015,2018,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,12 +17,27 @@ */ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(_WIN32) +#include +#include +#define F_OK 0 +#define access _access +#else +#include +#include +#include +#include +#endif -#include -#include -#include - -#include "DStarRepeaterLogRedirect.h" #include "DStarRepeaterTXRXThread.h" #include "RepeaterProtocolHandler.h" #include "DStarRepeaterTRXThread.h" @@ -49,6 +64,7 @@ #endif #include "DVAPController.h" #include "GMSKController.h" +#include "CallsignList.h" #include "DStarDefines.h" #include "Version.h" #include "Logger.h" @@ -56,329 +72,48 @@ #include "MQTTConnection.h" #endif -wxIMPLEMENT_APP(CDStarRepeaterApp); - -wxDEFINE_EVENT(wxEVT_THREAD_COMMAND, wxThreadEvent); - -wxBEGIN_EVENT_TABLE(CDStarRepeaterApp, wxApp) - EVT_THREAD(wxEVT_THREAD_COMMAND, CDStarRepeaterApp::OnRemoteCmd) -wxEND_EVENT_TABLE() - -const wxString NAME_PARAM = "Repeater Name"; -const wxString NOLOGGING_SWITCH = "nolog"; -const wxString GUI_SWITCH = "gui"; -const wxString LOGDIR_OPTION = "logdir"; -const wxString CONFDIR_OPTION = "confdir"; -const wxString AUDIODIR_OPTION = "audiodir"; -#if (wxUSE_GUI == 1) -const wxString LOG_BASE_NAME = "dstarrepeater"; -#else -const wxString LOG_BASE_NAME = "dstarrepeaterd"; -#endif - -CDStarRepeaterApp::CDStarRepeaterApp() : -wxApp(), -#if (wxUSE_GUI == 1) -m_frame(NULL), -#endif -m_name(), -m_nolog(false), -m_gui(false), -m_logDir(), -m_confDir(), -m_audioDir(), -m_thread(NULL), -m_config(NULL), -m_checker(NULL), -m_logChain(NULL) -{ -} - -CDStarRepeaterApp::~CDStarRepeaterApp() -{ -} - -bool CDStarRepeaterApp::OnInit() -{ - SetVendorName(VENDOR_NAME); - - if (!wxApp::OnInit()) - return false; - - if (!m_nolog) { - wxString logBaseName = LOG_BASE_NAME; - if (!m_name.IsEmpty()) { - logBaseName.Append("_"); - logBaseName.Append(m_name); - } - -#if defined(__WINDOWS__) - if (m_logDir.IsEmpty()) - m_logDir = ::wxGetHomeDir(); -#else - if (m_logDir.IsEmpty()) - m_logDir = LOG_DIR; -#endif - - try { - CLogger* log = new CLogger(m_logDir, logBaseName); - wxLog::SetActiveTarget(log); - } catch ( const std::runtime_error& e ) { - wxLog::SetActiveTarget(new wxLogStderr()); - wxLogError("Could not open log file, logging to stderr"); - } - } else { - new wxLogNull; - } - - m_logChain = new wxLogChain(new CDStarRepeaterLogRedirect); - - wxString appName; - if (!m_name.IsEmpty()) - appName = APPLICATION_NAME + " " + m_name; - else - appName = APPLICATION_NAME; - -#if !defined(__WINDOWS__) - appName.Replace(" ", "_"); - m_checker = new wxSingleInstanceChecker(appName, "/tmp"); -#else - m_checker = new wxSingleInstanceChecker(appName); -#endif - - bool ret = m_checker->IsAnotherRunning(); - if (ret) { - wxLogError("Another copy of the D-Star Repeater is running, exiting"); - return false; - } - -#if defined(__WINDOWS__) - if (m_confDir.IsEmpty()) - m_confDir = ::wxGetHomeDir(); - - m_config = new CDStarRepeaterConfig(new wxConfig(APPLICATION_NAME), m_confDir, CONFIG_FILE_NAME, m_name); -#else - if (m_confDir.IsEmpty()) - m_confDir = CONF_DIR; - - try { - m_config = new CDStarRepeaterConfig(m_confDir, CONFIG_FILE_NAME, m_name, true); - } catch( std::runtime_error& e ) { - wxLogError("Could not open configuration file"); - return false; - } -#endif - - wxString type; - m_config->getModem(type); - -#if (wxUSE_GUI == 1) - wxString frameName = APPLICATION_NAME + " (" + type + ") - "; - if (!m_name.IsEmpty()) { - frameName.Append(m_name); - frameName.Append(" - "); - } - frameName.Append(VERSION); - - wxPoint position = wxDefaultPosition; - - int x, y; - m_config->getPosition(x, y); - if (x >= 0 && y >= 0) - position = wxPoint(x, y); - - m_frame = new CDStarRepeaterFrame(frameName, type, position, m_gui); - m_frame->Show(); - - SetTopWindow(m_frame); -#endif - - wxLogInfo("Starting " + APPLICATION_NAME + " - " + VERSION); - - // Log the version of wxWidgets and the Operating System - wxLogInfo("Using wxWidgets %d.%d.%d on %s", wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER, ::wxGetOsDescription().c_str()); - -#if defined(MQTT) - wxString mqttHost, mqttUsername, mqttPassword, mqttName; - unsigned int mqttPort, mqttKeepalive; - bool mqttAuth; - m_config->getMQTT(mqttHost, mqttPort, mqttAuth, mqttUsername, mqttPassword, mqttKeepalive, mqttName); - - if (!mqttHost.IsEmpty()) { - std::vector> subscriptions; - - g_mqtt = new CMQTTConnection( - std::string(mqttHost.mb_str()), - (unsigned short)mqttPort, - std::string(mqttName.mb_str()), - mqttAuth, - std::string(mqttUsername.mb_str()), - std::string(mqttPassword.mb_str()), - subscriptions, - mqttKeepalive - ); - - bool ret = g_mqtt->open(); - if (!ret) { - wxLogError("Unable to start MQTT connection to %s:%u", mqttHost.c_str(), mqttPort); - delete g_mqtt; - g_mqtt = NULL; - } else { - wxLogInfo("MQTT connected to %s:%u as %s", mqttHost.c_str(), mqttPort, mqttName.c_str()); - } - } -#endif - - createThread(); - - return true; -} - -int CDStarRepeaterApp::OnExit() -{ - wxLogInfo(APPLICATION_NAME + " is exiting"); - - m_logChain->SetLog(NULL); - - m_thread->kill(); - m_thread->Wait(); - -#if defined(MQTT) - if (g_mqtt != NULL) { - g_mqtt->close(); - delete g_mqtt; - g_mqtt = NULL; - } -#endif - - delete m_config; +// --------------------------------------------------------------------------- +// Signal handling +// --------------------------------------------------------------------------- - delete m_checker; +static volatile sig_atomic_t g_running = 1; - return 0; -} - -void CDStarRepeaterApp::OnInitCmdLine(wxCmdLineParser& parser) -{ - parser.AddSwitch(NOLOGGING_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); - parser.AddSwitch(GUI_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); - parser.AddOption(LOGDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); - parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); - parser.AddOption(AUDIODIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); - parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); - - wxApp::OnInitCmdLine(parser); +#if defined(_WIN32) +static BOOL WINAPI consoleHandler(DWORD signal) { + if (signal == CTRL_C_EVENT || signal == CTRL_CLOSE_EVENT || signal == CTRL_BREAK_EVENT) { + g_running = 0; + return TRUE; + } + return FALSE; } - -bool CDStarRepeaterApp::OnCmdLineParsed(wxCmdLineParser& parser) -{ - if (!wxApp::OnCmdLineParsed(parser)) - return false; - - m_nolog = parser.Found(NOLOGGING_SWITCH); - m_gui = parser.Found(GUI_SWITCH); - - wxString logDir; - bool found = parser.Found(LOGDIR_OPTION, &logDir); - if (found) - m_logDir = logDir; - - wxString confDir; - found = parser.Found(CONFDIR_OPTION, &confDir); - if (found) - m_confDir = confDir; - - wxString audioDir; - found = parser.Found(AUDIODIR_OPTION, &audioDir); - if (found) - m_audioDir = audioDir; - else -// XXX Homedir here isn't appropriate. I don't know if logDir is either. -// XXX Need to look at more code -#if defined(__WINDOWS__) - m_audioDir = ::wxGetHomeDir(); #else - m_audioDir = logDir; -#endif - - if (parser.GetParamCount() > 0U) - m_name = parser.GetParam(0U); - - return true; -} - -#if defined(__WXDEBUG__) -void CDStarRepeaterApp::OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg) +static void handleSignal(int /*sig*/) { - wxLogFatalError("Assertion failed on line %d in file %s and function %s: %s %s", line, file, func, cond, msg); + g_running = 0; } #endif -CDStarRepeaterStatusData* CDStarRepeaterApp::getStatus() const -{ - return m_thread->getStatus(); -} - -void CDStarRepeaterApp::showLog(const wxString& text) -{ -#if (wxUSE_GUI == 1) - if(m_frame) - m_frame->showLog(text); -#endif -} +// --------------------------------------------------------------------------- +// createThread -- builds and launches the repeater thread +// --------------------------------------------------------------------------- -void CDStarRepeaterApp::setOutputs(bool out1, bool out2, bool out3, bool out4) +IDStarRepeaterThread* createThread(CDStarRepeaterConfig* config, + const std::string& audioDir, + std::string commandLine[6]) { - wxLogInfo("Output 1 = %d, output 2 = %d, output 3 = %d, output 4 = %d", int(out1), int(out2), int(out3), int(out4)); + assert(config != nullptr); - m_thread->setOutputs(out1, out2, out3, out4); -} + std::string callsign, gateway; + DSTAR_MODE mode; + ACK_TYPE ack; + bool restriction, rpt1Validation, dtmfBlanking, errorReply; + config->getCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); -void CDStarRepeaterApp::setLogging(bool logging) -{ - wxLogInfo("Frame logging set to %d, in %s", int(logging), m_audioDir.c_str()); + std::string modemType; + config->getModem(modemType); - m_thread->setLogging(logging, m_audioDir); -} - -void CDStarRepeaterApp::setPosition(int x, int y) -{ - m_config->setPosition(x, y); - m_config->write(); -} - -void CDStarRepeaterApp::OnRemoteCmd(wxThreadEvent& event) -{ - wxLogMessage("Request to execute command %s (%d)", m_commandLine[event.GetInt()], event.GetInt()); - // XXX sanity check the command line here. - wxShell(m_commandLine[event.GetInt()]); -} - -void CDStarRepeaterApp::startup() -{ - m_thread->startup(); -} - -void CDStarRepeaterApp::shutdown() -{ - m_thread->shutdown(); -} - -void CDStarRepeaterApp::createThread() -{ - wxASSERT(m_config != NULL); - - wxString callsign, gateway; - DSTAR_MODE mode; - ACK_TYPE ack; - bool restriction, rpt1Validation, dtmfBlanking, errorReply; - m_config->getCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); - - wxString modemType; - m_config->getModem(modemType); - - // DVAP can only do simplex, force the mode accordingly - if (modemType.IsSameAs("DVAP") || modemType.IsSameAs(wxT("Icom Access Point/Terminal Mode"))) { + // DVAP and Icom terminal mode can only do simplex -- adjust accordingly + if (modemType == "DVAP" || modemType == "Icom Access Point/Terminal Mode") { if (mode == MODE_DUPLEX) { wxLogInfo("Changing mode from DUPLEX to SIMPLEX"); mode = MODE_SIMPLEX; @@ -388,218 +123,261 @@ void CDStarRepeaterApp::createThread() } } - // XXX This should be m_thread eventually. + IDStarRepeaterThread* thread = nullptr; switch (mode) { case MODE_RXONLY: - m_thread = new CDStarRepeaterRXThread(modemType); + thread = new CDStarRepeaterRXThread(modemType); break; case MODE_TXONLY: - m_thread = new CDStarRepeaterTXThread(modemType); + thread = new CDStarRepeaterTXThread(modemType); break; case MODE_TXANDRX: - m_thread = new CDStarRepeaterTXRXThread(modemType); + thread = new CDStarRepeaterTXRXThread(modemType); break; default: - m_thread = new CDStarRepeaterTRXThread(modemType); + thread = new CDStarRepeaterTRXThread(modemType); break; } - m_thread->setCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); - wxLogInfo("Callsign set to \"%s\", gateway set to \"%s\", mode: %d, ack: %d, restriction: %d, RPT1 validation: %d, DTMF blanking: %d, Error reply: %d", callsign.c_str(), gateway.c_str(), int(mode), int(ack), int(restriction), int(rpt1Validation), int(dtmfBlanking), int(errorReply)); + thread->setCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); + wxLogInfo("Callsign set to \"%s\", gateway set to \"%s\", mode: %d, ack: %d, restriction: %d, RPT1 validation: %d, DTMF blanking: %d, Error reply: %d", + callsign.c_str(), gateway.c_str(), int(mode), int(ack), int(restriction), int(rpt1Validation), int(dtmfBlanking), int(errorReply)); - wxString gatewayAddress, localAddress, name; + std::string gatewayAddress, localAddress, netName; unsigned int gatewayPort, localPort; - m_config->getNetwork(gatewayAddress, gatewayPort, localAddress, localPort, name); - wxLogInfo("Gateway set to %s:%u, local set to %s:%u, name set to \"%s\"", gatewayAddress.c_str(), gatewayPort, localAddress.c_str(), localPort, name.c_str()); + config->getNetwork(gatewayAddress, gatewayPort, localAddress, localPort, netName); + wxLogInfo("Gateway set to %s:%u, local set to %s:%u, name set to \"%s\"", + gatewayAddress.c_str(), gatewayPort, localAddress.c_str(), localPort, netName.c_str()); - if (!gatewayAddress.IsEmpty()) { - bool local = gatewayAddress.IsSameAs("127.0.0.1"); + if (!gatewayAddress.empty()) { + bool local = (gatewayAddress == "127.0.0.1"); - CRepeaterProtocolHandler* handler = new CRepeaterProtocolHandler(gatewayAddress, gatewayPort, localAddress, localPort, name); + CRepeaterProtocolHandler* handler = new CRepeaterProtocolHandler( + gatewayAddress, gatewayPort, localAddress, localPort, netName); bool res = handler->open(); - if (!res) + if (!res) { wxLogError("Cannot open the protocol handler"); - else - m_thread->setProtocolHandler(handler, local); + delete handler; + } else { + thread->setProtocolHandler(handler, local); + } } unsigned int timeout, ackTime; - m_config->getTimes(timeout, ackTime); - m_thread->setTimes(timeout, ackTime); + config->getTimes(timeout, ackTime); + thread->setTimes(timeout, ackTime); wxLogInfo("Timeout set to %u secs, ack time set to %u ms", timeout, ackTime); unsigned int beaconTime; - wxString beaconText; - bool beaconVoice; - TEXT_LANG language; - m_config->getBeacon(beaconTime, beaconText, beaconVoice, language); + std::string beaconText; + bool beaconVoice; + TEXT_LANG language; + config->getBeacon(beaconTime, beaconText, beaconVoice, language); if (mode == MODE_GATEWAY) beaconTime = 0U; - m_thread->setBeacon(beaconTime, beaconText, beaconVoice, language); - wxLogInfo("Beacon set to %u mins, text set to \"%s\", voice set to %d, language set to %d", beaconTime / 60U, beaconText.c_str(), int(beaconVoice), int(language)); + thread->setBeacon(beaconTime, beaconText, beaconVoice, language); + wxLogInfo("Beacon set to %u mins, text set to \"%s\", voice set to %d, language set to %d", + beaconTime / 60U, beaconText.c_str(), int(beaconVoice), int(language)); - bool announcementEnabled; + bool announcementEnabled; unsigned int announcementTime; - wxString announcementRecordRPT1, announcementRecordRPT2; - wxString announcementDeleteRPT1, announcementDeleteRPT2; - m_config->getAnnouncement(announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); + std::string announcementRecordRPT1, announcementRecordRPT2; + std::string announcementDeleteRPT1, announcementDeleteRPT2; + config->getAnnouncement(announcementEnabled, announcementTime, + announcementRecordRPT1, announcementRecordRPT2, + announcementDeleteRPT1, announcementDeleteRPT2); if (mode == MODE_GATEWAY) announcementEnabled = false; - m_thread->setAnnouncement(announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); - wxLogInfo("Announcement enabled: %d, time: %u mins, record RPT1: \"%s\", record RPT2: \"%s\", delete RPT1: \"%s\", delete RPT2: \"%s\"", int(announcementEnabled), announcementTime / 60U, announcementRecordRPT1.c_str(), announcementRecordRPT2.c_str(), announcementDeleteRPT1.c_str(), announcementDeleteRPT2.c_str()); + thread->setAnnouncement(announcementEnabled, announcementTime, + announcementRecordRPT1, announcementRecordRPT2, + announcementDeleteRPT1, announcementDeleteRPT2); + wxLogInfo("Announcement enabled: %d, time: %u mins, record RPT1: \"%s\", record RPT2: \"%s\", delete RPT1: \"%s\", delete RPT2: \"%s\"", + int(announcementEnabled), announcementTime / 60U, + announcementRecordRPT1.c_str(), announcementRecordRPT2.c_str(), + announcementDeleteRPT1.c_str(), announcementDeleteRPT2.c_str()); wxLogInfo("Modem type set to \"%s\"", modemType.c_str()); - CModem* modem = NULL; - if (modemType.IsSameAs("DVAP")) { - wxString port; + CModem* modem = nullptr; + + if (modemType == "DVAP") { + std::string port; unsigned int frequency; - int power, squelch; - m_config->getDVAP(port, frequency, power, squelch); - wxLogInfo("DVAP: port: %s, frequency: %u Hz, power: %d dBm, squelch: %d dBm", port.c_str(), frequency, power, squelch); + int power, squelch; + config->getDVAP(port, frequency, power, squelch); + wxLogInfo("DVAP: port: %s, frequency: %u Hz, power: %d dBm, squelch: %d dBm", + port.c_str(), frequency, power, squelch); modem = new CDVAPController(port, frequency, power, squelch); - } else if (modemType.IsSameAs("DV-RPTR V1")) { - wxString port; - bool rxInvert, txInvert, channel; + + } else if (modemType == "DV-RPTR V1") { + std::string port; + bool rxInvert, txInvert, channel; unsigned int modLevel, txDelay; - m_config->getDVRPTR1(port, rxInvert, txInvert, channel, modLevel, txDelay); - wxLogInfo("DV-RPTR V1, port: %s, RX invert: %d, TX invert: %d, channel: %s, mod level: %u%%, TX delay: %u ms", port.c_str(), int(rxInvert), int(txInvert), channel ? "B" : "A", modLevel, txDelay); - modem = new CDVRPTRV1Controller(port, wxEmptyString, rxInvert, txInvert, channel, modLevel, txDelay); - } else if (modemType.IsSameAs("DV-RPTR V2")) { + config->getDVRPTR1(port, rxInvert, txInvert, channel, modLevel, txDelay); + wxLogInfo("DV-RPTR V1, port: %s, RX invert: %d, TX invert: %d, channel: %s, mod level: %u%%, TX delay: %u ms", + port.c_str(), int(rxInvert), int(txInvert), channel ? "B" : "A", modLevel, txDelay); + modem = new CDVRPTRV1Controller(port, std::string(), rxInvert, txInvert, channel, modLevel, txDelay); + + } else if (modemType == "DV-RPTR V2") { CONNECTION_TYPE connType; - wxString usbPort, address; - bool txInvert; - unsigned int port, modLevel, txDelay; - m_config->getDVRPTR2(connType, usbPort, address, port, txInvert, modLevel, txDelay); - wxLogInfo("DV-RPTR V2, type: %d, address: %s:%u, TX invert: %d, mod level: %u%%, TX delay: %u ms", int(connType), address.c_str(), port, int(txInvert), modLevel, txDelay); + std::string usbPort, address; + bool txInvert; + unsigned int port, modLevel, txDelay; + config->getDVRPTR2(connType, usbPort, address, port, txInvert, modLevel, txDelay); + wxLogInfo("DV-RPTR V2, type: %d, address: %s:%u, TX invert: %d, mod level: %u%%, TX delay: %u ms", + int(connType), address.c_str(), port, int(txInvert), modLevel, txDelay); + bool duplex = (mode == MODE_DUPLEX || mode == MODE_TXANDRX); switch (connType) { case CT_USB: - modem = new CDVRPTRV2Controller(usbPort, wxEmptyString, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + modem = new CDVRPTRV2Controller(usbPort, std::string(), txInvert, modLevel, duplex, callsign, txDelay); break; case CT_NETWORK: - modem = new CDVRPTRV2Controller(address, port, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + modem = new CDVRPTRV2Controller(address, port, txInvert, modLevel, duplex, callsign, txDelay); break; } - } else if (modemType.IsSameAs("DV-RPTR V3")) { + + } else if (modemType == "DV-RPTR V3") { CONNECTION_TYPE connType; - wxString usbPort, address; - bool txInvert; - unsigned int port, modLevel, txDelay; - m_config->getDVRPTR3(connType, usbPort, address, port, txInvert, modLevel, txDelay); - wxLogInfo("DV-RPTR V3, type: %d, address: %s:%u, TX invert: %d, mod level: %u%%, TX delay: %u ms", int(connType), address.c_str(), port, int(txInvert), modLevel, txDelay); + std::string usbPort, address; + bool txInvert; + unsigned int port, modLevel, txDelay; + config->getDVRPTR3(connType, usbPort, address, port, txInvert, modLevel, txDelay); + wxLogInfo("DV-RPTR V3, type: %d, address: %s:%u, TX invert: %d, mod level: %u%%, TX delay: %u ms", + int(connType), address.c_str(), port, int(txInvert), modLevel, txDelay); + bool duplex = (mode == MODE_DUPLEX || mode == MODE_TXANDRX); switch (connType) { case CT_USB: - modem = new CDVRPTRV3Controller(usbPort, wxEmptyString, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + modem = new CDVRPTRV3Controller(usbPort, std::string(), txInvert, modLevel, duplex, callsign, txDelay); break; case CT_NETWORK: - modem = new CDVRPTRV3Controller(address, port, txInvert, modLevel, mode == MODE_DUPLEX || mode == MODE_TXANDRX, callsign, txDelay); + modem = new CDVRPTRV3Controller(address, port, txInvert, modLevel, duplex, callsign, txDelay); break; } - } else if (modemType.IsSameAs("DVMEGA")) { - wxString port; + + } else if (modemType == "DVMEGA") { + std::string port; DVMEGA_VARIANT variant; - bool rxInvert, txInvert; - unsigned int txDelay, rxFrequency, txFrequency, power; - m_config->getDVMEGA(port, variant, rxInvert, txInvert, txDelay, rxFrequency, txFrequency, power); - wxLogInfo("DVMEGA, port: %s, variant: %d, RX invert: %d, TX invert: %d, TX delay: %u ms, rx frequency: %u Hz, tx frequency: %u Hz, power: %u %%", port.c_str(), int(variant), int(rxInvert), int(txInvert), txDelay, rxFrequency, txFrequency, power); + bool rxInvert, txInvert; + unsigned int txDelay, rxFrequency, txFrequency, power; + config->getDVMEGA(port, variant, rxInvert, txInvert, txDelay, rxFrequency, txFrequency, power); + wxLogInfo("DVMEGA, port: %s, variant: %d, RX invert: %d, TX invert: %d, TX delay: %u ms, rx frequency: %u Hz, tx frequency: %u Hz, power: %u %%", + port.c_str(), int(variant), int(rxInvert), int(txInvert), txDelay, rxFrequency, txFrequency, power); switch (variant) { case DVMV_MODEM: - modem = new CDVMegaController(port, wxEmptyString, rxInvert, txInvert, txDelay); + modem = new CDVMegaController(port, std::string(), rxInvert, txInvert, txDelay); break; case DVMV_RADIO_2M: case DVMV_RADIO_70CM: case DVMV_RADIO_2M_70CM: - modem = new CDVMegaController(port, wxEmptyString, txDelay, rxFrequency, txFrequency, power); + modem = new CDVMegaController(port, std::string(), txDelay, rxFrequency, txFrequency, power); break; default: wxLogError("Unknown DVMEGA variant - %d", int(variant)); break; } - } else if (modemType.IsSameAs("GMSK Modem")) { + + } else if (modemType == "GMSK Modem") { USB_INTERFACE iface; - unsigned int address; - m_config->getGMSK(iface, address); + unsigned int address; + config->getGMSK(iface, address); wxLogInfo("GMSK, interface: %d, address: %04X", int(iface), address); modem = new CGMSKController(iface, address, mode == MODE_DUPLEX || mode == MODE_TXANDRX); - } else if (modemType.IsSameAs("Sound Card")) { - wxString rxDevice, txDevice; - bool rxInvert, txInvert; - wxFloat32 rxLevel, txLevel; + + } else if (modemType == "Sound Card") { + std::string rxDevice, txDevice; + bool rxInvert, txInvert; + float rxLevel, txLevel; unsigned int txDelay, txTail; - m_config->getSoundCard(rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); - wxLogInfo("Sound Card, devices: %s:%s, invert: %d:%d, levels: %.2f:%.2f, tx delay: %u ms, tx tail: %u ms", rxDevice.c_str(), txDevice.c_str(), int(rxInvert), int(txInvert), rxLevel, txLevel, txDelay, txTail); + config->getSoundCard(rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); + wxLogInfo("Sound Card, devices: %s:%s, invert: %d:%d, levels: %.2f:%.2f, tx delay: %u ms, tx tail: %u ms", + rxDevice.c_str(), txDevice.c_str(), int(rxInvert), int(txInvert), rxLevel, txLevel, txDelay, txTail); modem = new CSoundCardController(rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); - } else if (modemType.IsSameAs("MMDVM")) { - wxString port; - bool rxInvert, txInvert, pttInvert; + + } else if (modemType == "MMDVM") { + std::string port; + bool rxInvert, txInvert, pttInvert; unsigned int txDelay, rxLevel, txLevel; - m_config->getMMDVM(port, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); - wxLogInfo("MMDVM, port: %s, RX invert: %d, TX invert: %d, PTT invert: %d, TX delay: %u ms, RX level: %u%%, TX level: %u%%", port.c_str(), int(rxInvert), int(txInvert), int(pttInvert), txDelay, rxLevel, txLevel); - modem = new CMMDVMController(port, wxEmptyString, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); - } else if (modemType.IsSameAs("Split")) { - wxString localAddress; - unsigned int localPort; - wxArrayString transmitterNames, receiverNames; - unsigned int timeout; - m_config->getSplit(localAddress, localPort, transmitterNames, receiverNames, timeout); - wxLogInfo("Split, local: %s:%u, timeout: %u ms", localAddress.c_str(), localPort, timeout); - for (unsigned int i = 0U; i < transmitterNames.GetCount(); i++) { - wxString name = transmitterNames.Item(i); - if (!name.IsEmpty()) { - wxLogInfo("\tTX %u name: %s", i + 1U, name.c_str()); - } + config->getMMDVM(port, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); + wxLogInfo("MMDVM, port: %s, RX invert: %d, TX invert: %d, PTT invert: %d, TX delay: %u ms, RX level: %u%%, TX level: %u%%", + port.c_str(), int(rxInvert), int(txInvert), int(pttInvert), txDelay, rxLevel, txLevel); + modem = new CMMDVMController(port, std::string(), rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); + + } else if (modemType == "Split") { + std::string localAddr; + unsigned int localP; + std::vector transmitterNames, receiverNames; + unsigned int splitTimeout; + config->getSplit(localAddr, localP, transmitterNames, receiverNames, splitTimeout); + wxLogInfo("Split, local: %s:%u, timeout: %u ms", localAddr.c_str(), localP, splitTimeout); + for (unsigned int i = 0U; i < transmitterNames.size(); i++) { + if (!transmitterNames[i].empty()) + wxLogInfo("\tTX %u name: %s", i + 1U, transmitterNames[i].c_str()); } - for (unsigned int i = 0U; i < receiverNames.GetCount(); i++) { - wxString name = receiverNames.Item(i); - if (!name.IsEmpty()) { - wxLogInfo("\tRX %u name: %s", i + 1U, name.c_str()); - } + for (unsigned int i = 0U; i < receiverNames.size(); i++) { + if (!receiverNames[i].empty()) + wxLogInfo("\tRX %u name: %s", i + 1U, receiverNames[i].c_str()); } - modem = new CSplitController(localAddress, localPort, transmitterNames, receiverNames, timeout); - } else if (modemType.IsSameAs("Icom Access Point/Terminal Mode")) { - wxString port; - m_config->getIcom(port); + modem = new CSplitController(localAddr, localP, transmitterNames, receiverNames, splitTimeout); + + } else if (modemType == "Icom Access Point/Terminal Mode") { + std::string port; + config->getIcom(port); wxLogInfo("Icom, port: %s", port.c_str()); modem = new CIcomController(port); + } else { wxLogError("Unknown modem type: %s", modemType.c_str()); } - if (modem != NULL) { + if (modem != nullptr) { bool res = modem->start(); - if (!res) + if (!res) { wxLogError("Cannot open the D-Star modem"); - else - m_thread->setModem(modem); + delete modem; + } else { + thread->setModem(modem); + } } - wxString controllerType; + std::string controllerType; unsigned int portConfig, activeHangTime; - bool pttInvert; - m_config->getController(controllerType, portConfig, pttInvert, activeHangTime); - wxLogInfo("Controller set to %s, config: %u, PTT invert: %d, active hang time: %u ms", controllerType.c_str(), portConfig, int(pttInvert), activeHangTime); - - CExternalController* controller = NULL; + bool pttInvert; + config->getController(controllerType, portConfig, pttInvert, activeHangTime); + wxLogInfo("Controller set to %s, config: %u, PTT invert: %d, active hang time: %u ms", + controllerType.c_str(), portConfig, int(pttInvert), activeHangTime); + + CExternalController* controller = nullptr; + + const std::string PREFIX_K8055 = "Velleman K8055 - "; + const std::string PREFIX_URIUSB = "URI USB - "; + const std::string PREFIX_SERIAL = "Serial - "; + const std::string PREFIX_ARDUINO = "Arduino - "; + + auto startsWith = [](const std::string& s, const std::string& prefix, std::string& rest) -> bool { + if (s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0) { + rest = s.substr(prefix.size()); + return true; + } + return false; + }; - wxString port; - if (controllerType.StartsWith("Velleman K8055 - ", &port)) { - unsigned long num; - port.ToULong(&num); + std::string portStr; + if (startsWith(controllerType, PREFIX_K8055, portStr)) { + unsigned long num = std::stoul(portStr); controller = new CExternalController(new CK8055Controller(num), pttInvert); - } else if (controllerType.StartsWith("URI USB - ", &port)) { - unsigned long num; - port.ToULong(&num); - controller = new CExternalController(new CURIUSBController(num, true), pttInvert); - } else if (controllerType.StartsWith("Serial - ", &port)) { - controller = new CExternalController(new CSerialLineController(port, portConfig), pttInvert); - } else if (controllerType.StartsWith("Arduino - ", &port)) { - controller = new CExternalController(new CArduinoController(port), pttInvert); + } else if (startsWith(controllerType, PREFIX_URIUSB, portStr)) { + unsigned long num = std::stoul(portStr); + controller = new CExternalController(new CURIUSBController(num, true), pttInvert); + } else if (startsWith(controllerType, PREFIX_SERIAL, portStr)) { + controller = new CExternalController(new CSerialLineController(portStr, portConfig), pttInvert); + } else if (startsWith(controllerType, PREFIX_ARDUINO, portStr)) { + controller = new CExternalController(new CArduinoController(portStr), pttInvert); #if defined(GPIO) - } else if (controllerType.IsSameAs("GPIO")) { + } else if (controllerType == "GPIO") { controller = new CExternalController(new CGPIOController(portConfig), pttInvert); - } else if (controllerType.IsSameAs(wxT("UDRC"))) { - switch(portConfig) { + } else if (controllerType == "UDRC") { + switch (portConfig) { case 1: controller = new CUDRCController(AUTO_FM); break; @@ -616,129 +394,368 @@ void CDStarRepeaterApp::createThread() case 2: controller = new CUDRCController(AUTO_AUTO); break; - - } + } #endif } else { - wxLogError("Unrecognized controller %s, using dummy controller", controllerType); + wxLogError("Unrecognized controller %s, using dummy controller", controllerType.c_str()); controller = new CExternalController(new CDummyController, pttInvert); } bool res = controller->open(); - if (!res) + if (!res) { wxLogError("Cannot open the hardware interface - %s", controllerType.c_str()); - else - m_thread->setController(controller, activeHangTime); + delete controller; + } else { + thread->setController(controller, activeHangTime); + } bool out1, out2, out3, out4; - m_config->getOutputs(out1, out2, out3, out4); - m_thread->setOutputs(out1, out2, out3, out4); -#if (wxUSE_GUI == 1) - m_frame->setOutputs(out1, out2, out3, out4); -#endif - wxLogInfo("Output 1 = %d, output 2 = %d, output 3 = %d, output 4 = %d", int(out1), int(out2), int(out3), int(out4)); + config->getOutputs(out1, out2, out3, out4); + thread->setOutputs(out1, out2, out3, out4); + wxLogInfo("Output 1 = %d, output 2 = %d, output 3 = %d, output 4 = %d", + int(out1), int(out2), int(out3), int(out4)); + + bool controlEnabled; + std::string rpt1Callsign, rpt2Callsign, ctrlShutdown, ctrlStartup; + std::vector status(5); + std::vector command(6); + std::vector output(4); + + config->getControl(controlEnabled, rpt1Callsign, rpt2Callsign, ctrlShutdown, ctrlStartup, + status[0], status[1], status[2], status[3], status[4], + command[0], commandLine[0], + command[1], commandLine[1], + command[2], commandLine[2], + command[3], commandLine[3], + command[4], commandLine[4], + command[5], commandLine[5], + output[0], output[1], output[2], output[3]); + + std::vector commandLineVec(commandLine, commandLine + 6); + thread->setControl(controlEnabled, rpt1Callsign, rpt2Callsign, ctrlShutdown, + ctrlStartup, command, commandLineVec, status, output); + + wxLogInfo("Control: enabled: %d, RPT1: %s, RPT2: %s, shutdown: %s, startup: %s, " + "status1: %s, status2: %s, status3: %s, status4: %s, status5: %s, " + "command1: %s = %s, command2: %s = %s, command3: %s = %s, " + "command4: %s = %s, command5: %s = %s, command6: %s = %s, " + "output1: %s, output2: %s, output3: %s, output4: %s", + int(controlEnabled), + rpt1Callsign.c_str(), rpt2Callsign.c_str(), + ctrlShutdown.c_str(), ctrlStartup.c_str(), + status[0].c_str(), status[1].c_str(), status[2].c_str(), status[3].c_str(), status[4].c_str(), + command[0].c_str(), commandLine[0].c_str(), + command[1].c_str(), commandLine[1].c_str(), + command[2].c_str(), commandLine[2].c_str(), + command[3].c_str(), commandLine[3].c_str(), + command[4].c_str(), commandLine[4].c_str(), + command[5].c_str(), commandLine[5].c_str(), + output[0].c_str(), output[1].c_str(), output[2].c_str(), output[3].c_str()); - bool enabled; - wxString rpt1Callsign, rpt2Callsign; - wxString shutdown, startup; + bool logging; + config->getLogging(logging); + thread->setLogging(logging, audioDir); + wxLogInfo("Frame logging set to %d, in %s", int(logging), audioDir.c_str()); + + // White list + { + std::string wlFile; + config->getWhitelist(wlFile); + if (!wlFile.empty() && access(wlFile.c_str(), F_OK) == 0) { + CCallsignList* list = new CCallsignList(wlFile); + bool ok = list->load(); + if (!ok) { + wxLogError("Unable to open white list file - %s", wlFile.c_str()); + delete list; + } else { + wxLogInfo("%u callsigns loaded into the white list", list->getCount()); + thread->setWhiteList(list); + } + } + } - // XXX Initialization should be temporary until we get them coming - // from m_config->getControl - wxArrayString status; - status.Add("", 5); - wxArrayString command; - command.Add("", 6); - wxArrayString output; - output.Add("", 4); + // Black list + { + std::string blFile; + config->getBlacklist(blFile); + if (!blFile.empty() && access(blFile.c_str(), F_OK) == 0) { + CCallsignList* list = new CCallsignList(blFile); + bool ok = list->load(); + if (!ok) { + wxLogError("Unable to open black list file - %s", blFile.c_str()); + delete list; + } else { + wxLogInfo("%u callsigns loaded into the black list", list->getCount()); + thread->setBlackList(list); + } + } + } - m_config->getControl(enabled, rpt1Callsign, rpt2Callsign, shutdown, startup, status[0], status[1], status[2], status[3], status[4], command[0], m_commandLine[0], command[1], m_commandLine[1], command[2], m_commandLine[2], command[3], m_commandLine[3], command[4], m_commandLine[4], command[5], m_commandLine[5], output[0], output[1], output[2], output[3]); + // Grey list + { + std::string glFile; + config->getGreylist(glFile); + if (!glFile.empty() && access(glFile.c_str(), F_OK) == 0) { + CCallsignList* list = new CCallsignList(glFile); + bool ok = list->load(); + if (!ok) { + wxLogError("Unable to open grey list file - %s", glFile.c_str()); + delete list; + } else { + wxLogInfo("%u callsigns loaded into the grey list", list->getCount()); + thread->setGreyList(list); + } + } + } - m_thread->setControl(enabled, rpt1Callsign, rpt2Callsign, shutdown, - startup, command, status, output); + // Launch the repeater thread (virtual dispatch through lambda) + thread->m_thread = std::thread([thread]() { thread->entry(); }); - wxLogInfo(wxT("Control: enabled: %d, RPT1: %s, RPT2: %s, shutdown: %s, startup: %s, status1: %s, status2: %s, status3: %s, status4: %s, status5: %s, command1: %s = %s, command2: %s = %s, command3: %s = %s, command4: %s = %s, command5: %s = %s, command6: %s = %s, output1: %s, output2: %s, output3: %s, output4: %s"), enabled, rpt1Callsign.c_str(), rpt2Callsign.c_str(), shutdown.c_str(), startup.c_str(), status[0].c_str(), status[1].c_str(), status[2].c_str(), status[3].c_str(), status[4].c_str(), command[0].c_str(), m_commandLine[0].c_str(), command[1].c_str(), m_commandLine[1].c_str(), command[2].c_str(), m_commandLine[2].c_str(), command[3].c_str(), m_commandLine[3].c_str(), command[4].c_str(), m_commandLine[4].c_str(), command[5].c_str(), m_commandLine[5].c_str(), output[0].c_str(), output[1].c_str(), output[2].c_str(), output[3].c_str()); + return thread; +} - bool logging; - m_config->getLogging(logging); - m_thread->setLogging(logging, m_audioDir); -#if (wxUSE_GUI == 1) - m_frame->setLogging(logging); +// --------------------------------------------------------------------------- +// main +// --------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ + if (argc < 2) { + std::fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + const std::string configFile = argv[1]; + + // ----------------------------------------------------------------------- + // Configuration -- must be loaded before logger so we can read [Log] + // ----------------------------------------------------------------------- + + CDStarRepeaterConfig* config = nullptr; + try { + config = new CDStarRepeaterConfig(configFile); + } catch (const std::exception& e) { + std::fprintf(stderr, "Could not open configuration file %s: %s\n", + configFile.c_str(), e.what()); + return 1; + } + + // ----------------------------------------------------------------------- + // Logging -- driven entirely by [Log] in the config file + // ----------------------------------------------------------------------- + + { + std::string logFilePath; + unsigned int logFileLevel, logDisplayLevel, logMQTTLevel; + config->getLog(logFilePath, logFileLevel, logDisplayLevel, logMQTTLevel); + +#if defined(MQTT) + g_mqttLevel = logMQTTLevel; #endif - wxLogInfo("Frame logging set to %d, in %s", int(logging), m_audioDir.c_str()); -#if defined(__WINDOWS__) - wxFileName wlFilename(wxFileName::GetHomeDir(), PRIMARY_WHITELIST_FILE_NAME); + const bool wantFile = (logFileLevel != 0U); + const bool wantDisplay = (logDisplayLevel != 0U); + + if (wantFile || wantDisplay) { + const std::string logBaseName = "dstarrepeaterd"; + // Pass empty directory when file logging is disabled so the logger + // skips opening a file while still writing to stdout. + const std::string logDir = wantFile ? logFilePath : std::string(); + try { + CLogger::setInstance(new CLogger(logDir, logBaseName, logFileLevel, logDisplayLevel)); + } catch (const std::exception& e) { + std::fprintf(stderr, "Could not open log file in %s: %s -- logging to stderr only\n", + logFilePath.c_str(), e.what()); + } + } + // When both levels are 0, no CLogger is installed; the wxLogXxx macros + // silently drop output because getInstance() returns nullptr. + } + + // ----------------------------------------------------------------------- + // Single-instance PID file lock + // ----------------------------------------------------------------------- + + std::string pidName = APPLICATION_NAME; + for (char& ch : pidName) + if (ch == ' ') ch = '_'; + +#if defined(_WIN32) + HANDLE hMutex = CreateMutexA(nullptr, TRUE, pidName.c_str()); + if (GetLastError() == ERROR_ALREADY_EXISTS) { + wxLogError("Another copy of the D-Star Repeater is running, exiting"); + delete config; + return 1; + } +#else + std::string pidPath = "/var/run/" + pidName + ".pid"; +#ifdef O_NOFOLLOW + int pidFd = open(pidPath.c_str(), O_RDWR | O_CREAT | O_NOFOLLOW, 0600); #else - wxFileName wlFilename(CONF_DIR, PRIMARY_WHITELIST_FILE_NAME); + int pidFd = open(pidPath.c_str(), O_RDWR | O_CREAT, 0600); #endif - bool exists = wlFilename.FileExists(); + if (pidFd < 0) { + wxLogError("Cannot create PID file %s", pidPath.c_str()); + delete config; + return 1; + } + if (flock(pidFd, LOCK_EX | LOCK_NB) < 0) { + wxLogError("Another copy of the D-Star Repeater is running, exiting"); + close(pidFd); + delete config; + return 1; + } - if (!exists) { -#if defined(__WINDOWS__) - wlFilename.Assign(wxFileName::GetHomeDir(), SECONDARY_WHITELIST_FILE_NAME); -#else - wlFilename.Assign(CONF_DIR, SECONDARY_WHITELIST_FILE_NAME); + // Write our PID to the file for service managers + char pidBuf[16]; + int pidLen = ::snprintf(pidBuf, sizeof(pidBuf), "%d\n", (int)::getpid()); + if (::ftruncate(pidFd, 0) == 0) { + ssize_t ret = ::write(pidFd, pidBuf, pidLen); + if (ret < 0) + ::fprintf(stderr, "Failed to write PID file\n"); + } #endif - exists = wlFilename.FileExists(); + + // ----------------------------------------------------------------------- + // OS identification + // ----------------------------------------------------------------------- + +#if defined(_WIN32) + wxLogInfo("Running on Windows"); +#else + struct utsname unameInfo; + if (uname(&unameInfo) == 0) { + wxLogInfo("Using %s %s on %s", unameInfo.sysname, unameInfo.release, unameInfo.machine); } +#endif - if (exists) { - CCallsignList* list = new CCallsignList(wlFilename.GetFullPath()); - bool res = list->load(); - if (!res) { - wxLogError("Unable to open white list file - %s", wlFilename.GetFullPath().c_str()); - delete list; - } else { - wxLogInfo("%u callsigns loaded into the white list", list->getCount()); - m_thread->setWhiteList(list); + wxLogInfo("Starting %s - %s", APPLICATION_NAME.c_str(), VERSION.c_str()); + wxLogInfo("Config file: %s", configFile.c_str()); + + // ----------------------------------------------------------------------- + // MQTT (optional) + // ----------------------------------------------------------------------- + +#if defined(MQTT) + { + std::string mqttHost, mqttUsername, mqttPassword, mqttName; + unsigned int mqttPort, mqttKeepalive; + bool mqttAuth; + config->getMQTT(mqttHost, mqttPort, mqttAuth, mqttUsername, mqttPassword, mqttKeepalive, mqttName); + + if (!mqttHost.empty()) { + std::vector> subscriptions; + + g_mqtt = new CMQTTConnection( + mqttHost, + static_cast(mqttPort), + mqttName, + mqttAuth, + mqttUsername, + mqttPassword, + subscriptions, + mqttKeepalive); + + bool ok = g_mqtt->open(); + if (!ok) { + wxLogError("Unable to start MQTT connection to %s:%u", mqttHost.c_str(), mqttPort); + delete g_mqtt; + g_mqtt = nullptr; + } else { + wxLogInfo("MQTT connected to %s:%u as %s", mqttHost.c_str(), mqttPort, mqttName.c_str()); + } } } -#if defined(__WINDOWS__) - wxFileName blFilename(wxFileName::GetHomeDir(), PRIMARY_BLACKLIST_FILE_NAME); +#endif + + // ----------------------------------------------------------------------- + // Audio directory (from config [Paths]) + // ----------------------------------------------------------------------- + + std::string dataDir, audioDir; + config->getPaths(dataDir, audioDir); + (void)dataDir; // DATA_DIR is baked in at compile time for BeaconUnit + + // ----------------------------------------------------------------------- + // Signal handlers + // ----------------------------------------------------------------------- + +#if defined(_WIN32) + SetConsoleCtrlHandler(consoleHandler, TRUE); #else - wxFileName blFilename(CONF_DIR, PRIMARY_BLACKLIST_FILE_NAME); + struct sigaction sa; + std::memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handleSignal; + sigemptyset(&sa.sa_mask); + sigaction(SIGTERM, &sa, nullptr); + sigaction(SIGINT, &sa, nullptr); #endif - exists = blFilename.FileExists(); - if (!exists) { -#if defined(__WINDOWS__) - blFilename.Assign(wxFileName::GetHomeDir(), SECONDARY_BLACKLIST_FILE_NAME); + // ----------------------------------------------------------------------- + // Create and run the repeater thread + // ----------------------------------------------------------------------- + + std::string commandLine[6]; + IDStarRepeaterThread* thread = nullptr; + try { + thread = createThread(config, audioDir, commandLine); + } catch (const std::exception& e) { + wxLogError("Failed to create repeater thread: %s", e.what()); + delete config; +#if defined(MQTT) + if (g_mqtt != nullptr) { + g_mqtt->close(); + delete g_mqtt; + g_mqtt = nullptr; + } +#endif +#if defined(_WIN32) + ReleaseMutex(hMutex); + CloseHandle(hMutex); #else - blFilename.Assign(CONF_DIR, SECONDARY_BLACKLIST_FILE_NAME); + flock(pidFd, LOCK_UN); + close(pidFd); + unlink(pidPath.c_str()); #endif - exists = blFilename.FileExists(); + delete CLogger::getInstance(); + CLogger::setInstance(nullptr); + return 1; } - if (exists) { - CCallsignList* list = new CCallsignList(blFilename.GetFullPath()); - bool res = list->load(); - if (!res) { - wxLogError("Unable to open black list file - %s", blFilename.GetFullPath().c_str()); - delete list; - } else { - wxLogInfo("%u callsigns loaded into the black list", list->getCount()); - m_thread->setBlackList(list); - } + // ----------------------------------------------------------------------- + // Main loop -- block until signalled + // ----------------------------------------------------------------------- + + while (g_running) + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // ----------------------------------------------------------------------- + // Shutdown + // ----------------------------------------------------------------------- + + wxLogInfo("%s is exiting", APPLICATION_NAME.c_str()); + + thread->kill(); + thread->m_thread.join(); + delete thread; + +#if defined(MQTT) + if (g_mqtt != nullptr) { + g_mqtt->close(); + delete g_mqtt; + g_mqtt = nullptr; } -#if defined(__WINDOWS__) - wxFileName glFilename(wxFileName::GetHomeDir(), GREYLIST_FILE_NAME); +#endif + + delete config; + +#if defined(_WIN32) + ReleaseMutex(hMutex); + CloseHandle(hMutex); #else - wxFileName glFilename(CONF_DIR, GREYLIST_FILE_NAME); + flock(pidFd, LOCK_UN); + close(pidFd); + unlink(pidPath.c_str()); #endif - exists = glFilename.FileExists(); - if (exists) { - CCallsignList* list = new CCallsignList(glFilename.GetFullPath()); - bool res = list->load(); - if (!res) { - wxLogError("Unable to open grey list file - %s", glFilename.GetFullPath().c_str()); - delete list; - } else { - wxLogInfo("%u callsigns loaded into the grey list", list->getCount()); - m_thread->setGreyList(list); - } - } - m_thread->Create(); - m_thread->SetPriority(wxPRIORITY_MAX); - m_thread->Run(); + return 0; } diff --git a/DStarRepeater/DStarRepeaterApp.h b/DStarRepeater/DStarRepeaterApp.h index 6c29dc8..69d2bee 100644 --- a/DStarRepeater/DStarRepeaterApp.h +++ b/DStarRepeater/DStarRepeaterApp.h @@ -19,72 +19,16 @@ #ifndef DStarRepeaterApp_H #define DStarRepeaterApp_H -#include -#include - #include "DStarRepeaterThread.h" -#include "DStarRepeaterStatusData.h" #include "DStarRepeaterConfig.h" -#if (wxUSE_GUI == 1) -#include "DStarRepeaterFrame.h" -#endif #include "DStarRepeaterDefs.h" -wxDECLARE_EVENT(wxEVT_THREAD_COMMAND, wxThreadEvent); - -class CDStarRepeaterApp : public wxApp { -public: - CDStarRepeaterApp(); - virtual ~CDStarRepeaterApp(); - - virtual bool OnInit(); - virtual int OnExit(); - - virtual void OnInitCmdLine(wxCmdLineParser& parser); - virtual bool OnCmdLineParsed(wxCmdLineParser& parser); - - // This is overridden because dialog boxes from threads are bad news -#if defined(__WXDEBUG__) - virtual void OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg); -#endif - - virtual CDStarRepeaterStatusData* getStatus() const; - - virtual void showLog(const wxString& text); - - virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); - - virtual void setLogging(bool logging); - - virtual void setPosition(int x, int y); - - void startup(); - void shutdown(); - -private: -#if (wxUSE_GUI == 1) - CDStarRepeaterFrame* m_frame; -#endif - - wxString m_name; - bool m_nolog; - bool m_gui; - wxString m_logDir; - wxString m_confDir; - wxString m_audioDir; - IDStarRepeaterThread* m_thread; - CDStarRepeaterConfig* m_config; - wxSingleInstanceChecker* m_checker; - wxLogChain* m_logChain; - wxString m_commandLine[6]; - - void createThread(); - - void OnRemoteCmd(wxThreadEvent& event); - - wxDECLARE_EVENT_TABLE(); -}; - -wxDECLARE_APP(CDStarRepeaterApp); +// Creates the repeater thread based on the configured mode, attaches all +// subsystems (modem, controller, lists, etc.) and launches it. Returns the +// newly allocated thread on success, or nullptr on failure (caller owns the +// pointer and must join/delete it on exit). +IDStarRepeaterThread* createThread(CDStarRepeaterConfig* config, + const std::string& audioDir, + std::string commandLine[6]); #endif diff --git a/DStarRepeater/DStarRepeaterDefs.h b/DStarRepeater/DStarRepeaterDefs.h index 0ca8e63..611ffbe 100644 --- a/DStarRepeater/DStarRepeaterDefs.h +++ b/DStarRepeater/DStarRepeaterDefs.h @@ -16,24 +16,23 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +// Application-level constants for the D-Star Repeater daemon. +// The config file path is now supplied as a required command-line argument +// rather than being constructed from compile-time macros. + #ifndef DStarRepeaterDefs_H #define DStarRepeaterDefs_H -#include - -const wxString APPLICATION_NAME = wxT("D-Star Repeater"); -const wxString CONFIG_FILE_NAME = wxT("dstarrepeater"); +#include "StdCompat.h" -const wxString PRIMARY_WHITELIST_FILE_NAME = wxT("rptr_whitelist.dat"); -const wxString SECONDARY_WHITELIST_FILE_NAME = wxT("whitelist.dat"); -const wxString PRIMARY_BLACKLIST_FILE_NAME = wxT("rptr_blacklist.dat"); -const wxString SECONDARY_BLACKLIST_FILE_NAME = wxT("blacklist.dat"); -const wxString GREYLIST_FILE_NAME = wxT("greylist.dat"); +inline const std::string APPLICATION_NAME = "D-Star Repeater"; +// Per-frame receive state machine used inside the repeater thread to parse the +// incoming bit stream once a valid header has been detected. enum DSTAR_RX_STATE { - DSRXS_LISTENING, - DSRXS_PROCESS_DATA, - DSRXS_PROCESS_SLOW_DATA + DSRXS_LISTENING, // Waiting for a DATA_SYNC pattern + DSRXS_PROCESS_DATA, // Consuming DV frame payload bytes + DSRXS_PROCESS_SLOW_DATA // Decoding the 3-byte slow-data section of a frame }; #endif diff --git a/DStarRepeater/DStarRepeaterFrame.cpp b/DStarRepeater/DStarRepeaterFrame.cpp deleted file mode 100644 index c41a54c..0000000 --- a/DStarRepeater/DStarRepeaterFrame.cpp +++ /dev/null @@ -1,586 +0,0 @@ -/* - * Copyright (C) 2011-2015,2018 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterFrame.h" -#include "DStarRepeaterApp.h" -#include "DStarDefines.h" -#include "LogEvent.h" -#include "Version.h" - -#include - -DEFINE_EVENT_TYPE(LOG_EVENT) - -enum { - Menu_File_Logging = 6000, - - Menu_View_Updates, - - Menu_Action_Shutdown, - Menu_Action_Startup, - Menu_Action_Command1, - Menu_Action_Command2, - Menu_Action_Command3, - Menu_Action_Command4, - Menu_Action_Command5, - Menu_Action_Command6, - - Menu_Output_1, - Menu_Output_2, - Menu_Output_3, - Menu_Output_4 -}; - -BEGIN_EVENT_TABLE(CDStarRepeaterFrame, wxFrame) - EVT_MENU(Menu_File_Logging, CDStarRepeaterFrame::onLogging) - EVT_MENU(wxID_EXIT, CDStarRepeaterFrame::onQuit) - EVT_MENU(Menu_View_Updates, CDStarRepeaterFrame::onUpdates) - EVT_MENU(wxID_ABOUT, CDStarRepeaterFrame::onAbout) - - EVT_MENU(Menu_Action_Startup, CDStarRepeaterFrame::onStatus) - EVT_MENU(Menu_Action_Shutdown, CDStarRepeaterFrame::onStatus) - - EVT_MENU(Menu_Action_Command1, CDStarRepeaterFrame::onActions) - EVT_MENU(Menu_Action_Command2, CDStarRepeaterFrame::onActions) - EVT_MENU(Menu_Action_Command3, CDStarRepeaterFrame::onActions) - EVT_MENU(Menu_Action_Command4, CDStarRepeaterFrame::onActions) - EVT_MENU(Menu_Action_Command5, CDStarRepeaterFrame::onActions) - EVT_MENU(Menu_Action_Command6, CDStarRepeaterFrame::onActions) - - EVT_MENU(Menu_Output_1, CDStarRepeaterFrame::onOutputs) - EVT_MENU(Menu_Output_2, CDStarRepeaterFrame::onOutputs) - EVT_MENU(Menu_Output_3, CDStarRepeaterFrame::onOutputs) - EVT_MENU(Menu_Output_4, CDStarRepeaterFrame::onOutputs) - - EVT_CLOSE(CDStarRepeaterFrame::onClose) - - EVT_TIMER(-1, CDStarRepeaterFrame::onTimer) - - EVT_CUSTOM(LOG_EVENT, wxID_ANY, CDStarRepeaterFrame::onLog) -END_EVENT_TABLE() - -#if defined(__WINDOWS__) -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH = 100U; -const unsigned int LABEL_WIDTH = 70U; -const unsigned int LOGTEXT_WIDTH = 560U; -#else -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH = 150U; -const unsigned int LABEL_WIDTH = 70U; -const unsigned int LOGTEXT_WIDTH = 710U; -#endif - -CDStarRepeaterFrame::CDStarRepeaterFrame(const wxString& title, const wxString& type, const wxPoint& position, bool gui) : -wxFrame(NULL, -1, title, position), -m_logging(NULL), -m_rxState(NULL), -m_rptState(NULL), -m_tx(NULL), -m_squelch(NULL), -m_signal(NULL), -m_your(NULL), -m_my(NULL), -m_rpt1(NULL), -m_rpt2(NULL), -m_flags(NULL), -m_percent(NULL), -m_timeout(NULL), -m_beacon(NULL), -m_announce(NULL), -m_text(NULL), -m_status1(NULL), -m_status2(NULL), -m_status3(NULL), -m_status4(NULL), -m_status5(NULL), -m_logLine1(NULL), -m_logLine2(NULL), -m_logLine3(NULL), -m_timer(this), -m_outputMenu(NULL), -#if defined(__WXDEBUG__) -m_updates(true) -#else -m_updates(gui) -#endif -{ - SetMenuBar(createMenuBar()); - - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - - wxPanel* panel = new wxPanel(this); - - wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL); - - wxStaticBoxSizer* stat1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Status")), wxVERTICAL); - wxFlexGridSizer* stat2Sizer = new wxFlexGridSizer(6); - - wxStaticText* rxStateLabel = new wxStaticText(panel, -1, wxT("RX State:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - stat2Sizer->Add(rxStateLabel, 0, wxALL, BORDER_SIZE); - - m_rxState = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - stat2Sizer->Add(m_rxState, 0, wxALL, BORDER_SIZE); - - wxStaticText* rptStateLabel = new wxStaticText(panel, -1, wxT("Rpt State:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - stat2Sizer->Add(rptStateLabel, 0, wxALL, BORDER_SIZE); - - m_rptState = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - stat2Sizer->Add(m_rptState, 0, wxALL, BORDER_SIZE); - - wxStaticText* txLabel = new wxStaticText(panel, -1, wxT("TX:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - stat2Sizer->Add(txLabel, 0, wxALL, BORDER_SIZE); - - m_tx = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - stat2Sizer->Add(m_tx, 0, wxALL, BORDER_SIZE); - - stat1Sizer->Add(stat2Sizer); - panelSizer->Add(stat1Sizer, 0, wxALL, BORDER_SIZE); - - if (type.IsSameAs(wxT("DVAP"))) { - wxStaticBoxSizer* rx1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Receiver")), wxVERTICAL); - wxFlexGridSizer* rx2Sizer = new wxFlexGridSizer(6); - - wxStaticText* squelchLabel = new wxStaticText(panel, -1, wxT("Squelch:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - rx2Sizer->Add(squelchLabel, 0, wxALL, BORDER_SIZE); - - m_squelch = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - rx2Sizer->Add(m_squelch, 0, wxALL, BORDER_SIZE); - - wxStaticText* signalLabel = new wxStaticText(panel, -1, wxT("Signal:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - rx2Sizer->Add(signalLabel, 0, wxALL, BORDER_SIZE); - - m_signal = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - rx2Sizer->Add(m_signal, 0, wxALL, BORDER_SIZE); - - wxStaticText* dummy20Label = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LABEL_WIDTH, -1)); - rx2Sizer->Add(dummy20Label, 0, wxALL, BORDER_SIZE); - - wxStaticText* dummy21Label = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - rx2Sizer->Add(dummy21Label, 0, wxALL, BORDER_SIZE); - - rx1Sizer->Add(rx2Sizer); - panelSizer->Add(rx1Sizer, 0, wxALL, BORDER_SIZE); - } - - wxStaticBoxSizer* info1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Header")), wxVERTICAL); - wxFlexGridSizer* info2Sizer = new wxFlexGridSizer(6); - - wxStaticText* yourLabel = new wxStaticText(panel, -1, wxT("UR:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - info2Sizer->Add(yourLabel, 0, wxALL, BORDER_SIZE); - - m_your = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - info2Sizer->Add(m_your, 0, wxALL, BORDER_SIZE); - - wxStaticText* rpt1Label = new wxStaticText(panel, -1, wxT("RPT1:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - info2Sizer->Add(rpt1Label, 0, wxALL, BORDER_SIZE); - - m_rpt1 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - info2Sizer->Add(m_rpt1, 0, wxALL, BORDER_SIZE); - - wxStaticText* rpt2Label = new wxStaticText(panel, -1, wxT("RPT2:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - info2Sizer->Add(rpt2Label, 0, wxALL, BORDER_SIZE); - - m_rpt2 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - info2Sizer->Add(m_rpt2, 0, wxALL, BORDER_SIZE); - - wxStaticText* myLabel = new wxStaticText(panel, -1, wxT("MY:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - info2Sizer->Add(myLabel, 0, wxALL, BORDER_SIZE); - - m_my = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - info2Sizer->Add(m_my, 0, wxALL, BORDER_SIZE); - - wxStaticText* flagsLabel = new wxStaticText(panel, -1, _("Flags:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - info2Sizer->Add(flagsLabel, 0, wxALL, BORDER_SIZE); - - m_flags = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LABEL_WIDTH, -1)); - info2Sizer->Add(m_flags, 0, wxALL, BORDER_SIZE); - - wxStaticText* percentLabel = new wxStaticText(panel, -1, _("Loss/BER:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - info2Sizer->Add(percentLabel, 0, wxALL, BORDER_SIZE); - - m_percent = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LABEL_WIDTH, -1)); - info2Sizer->Add(m_percent, 0, wxALL, BORDER_SIZE); - - info1Sizer->Add(info2Sizer); - panelSizer->Add(info1Sizer, 0, wxALL, BORDER_SIZE); - - wxStaticBoxSizer* timer1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Timers")), wxVERTICAL); - wxFlexGridSizer* timer2Sizer = new wxFlexGridSizer(6); - - wxStaticText* timeoutLabel = new wxStaticText(panel, -1, _("Timeout:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - timer2Sizer->Add(timeoutLabel, 0, wxALL, BORDER_SIZE); - - m_timeout = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - timer2Sizer->Add(m_timeout, 0, wxALL, BORDER_SIZE); - - wxStaticText* beaconLabel = new wxStaticText(panel, -1, _("Beacon:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - timer2Sizer->Add(beaconLabel, 0, wxALL, BORDER_SIZE); - - m_beacon = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - timer2Sizer->Add(m_beacon, 0, wxALL, BORDER_SIZE); - - wxStaticText* announceLabel = new wxStaticText(panel, -1, _("Announce:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - timer2Sizer->Add(announceLabel, 0, wxALL, BORDER_SIZE); - - m_announce = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - timer2Sizer->Add(m_announce, 0, wxALL, BORDER_SIZE); - - timer1Sizer->Add(timer2Sizer); - panelSizer->Add(timer1Sizer, 0, wxALL, BORDER_SIZE); - - wxStaticBoxSizer* text1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Gateway")), wxVERTICAL); - wxFlexGridSizer* text2Sizer = new wxFlexGridSizer(6); - - wxStaticText* textLabel = new wxStaticText(panel, -1, _("Ack Text:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - text2Sizer->Add(textLabel, 0, wxALL, BORDER_SIZE); - - m_text = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - text2Sizer->Add(m_text, 0, wxALL, BORDER_SIZE); - - wxStaticText* status1Label = new wxStaticText(panel, -1, _("Status 1:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - text2Sizer->Add(status1Label, 0, wxALL, BORDER_SIZE); - - m_status1 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - text2Sizer->Add(m_status1, 0, wxALL, BORDER_SIZE); - - wxStaticText* status2Label = new wxStaticText(panel, -1, _("Status 2:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - text2Sizer->Add(status2Label, 0, wxALL, BORDER_SIZE); - - m_status2 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - text2Sizer->Add(m_status2, 0, wxALL, BORDER_SIZE); - - wxStaticText* status3Label = new wxStaticText(panel, -1, _("Status 3:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - text2Sizer->Add(status3Label, 0, wxALL, BORDER_SIZE); - - m_status3 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - text2Sizer->Add(m_status3, 0, wxALL, BORDER_SIZE); - - wxStaticText* status4Label = new wxStaticText(panel, -1, _("Status 4:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - text2Sizer->Add(status4Label, 0, wxALL, BORDER_SIZE); - - m_status4 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - text2Sizer->Add(m_status4, 0, wxALL, BORDER_SIZE); - - wxStaticText* status5Label = new wxStaticText(panel, -1, _("Status 5:"), wxDefaultPosition, wxSize(LABEL_WIDTH, -1), wxALIGN_RIGHT); - text2Sizer->Add(status5Label, 0, wxALL, BORDER_SIZE); - - m_status5 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - text2Sizer->Add(m_status5, 0, wxALL, BORDER_SIZE); - - text1Sizer->Add(text2Sizer); - panelSizer->Add(text1Sizer, 0, wxALL, BORDER_SIZE); - - wxStaticBoxSizer* log1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Log")), wxVERTICAL); - wxBoxSizer* log2Sizer = new wxBoxSizer(wxVERTICAL); - - m_logLine1 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LOGTEXT_WIDTH, -1)); - m_logLine1->Wrap(LOGTEXT_WIDTH); - log2Sizer->Add(m_logLine1, 0, wxTOP | wxLEFT | wxRIGHT, BORDER_SIZE); - - m_logLine2 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LOGTEXT_WIDTH, -1)); - m_logLine2->Wrap(LOGTEXT_WIDTH); - log2Sizer->Add(m_logLine2, 0, wxLEFT | wxRIGHT, BORDER_SIZE); - - m_logLine3 = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LOGTEXT_WIDTH, -1)); - m_logLine3->Wrap(LOGTEXT_WIDTH); - log2Sizer->Add(m_logLine3, 0, wxLEFT | wxRIGHT | wxBOTTOM, BORDER_SIZE); - - log1Sizer->Add(log2Sizer); - panelSizer->Add(log1Sizer, 0, wxALL, BORDER_SIZE); - - panel->SetSizer(panelSizer); - panelSizer->SetSizeHints(panel); - - mainSizer->Add(panel); - - SetSizer(mainSizer); - mainSizer->SetSizeHints(this); - - m_timer.Start(1000); -} - -CDStarRepeaterFrame::~CDStarRepeaterFrame() -{ -} - -wxMenuBar* CDStarRepeaterFrame::createMenuBar() -{ - wxMenu* fileMenu = new wxMenu(); - m_logging = fileMenu->AppendCheckItem(Menu_File_Logging, _("Logging")); - fileMenu->AppendSeparator(); - fileMenu->Append(wxID_EXIT, _("Exit")); - - wxMenu* viewMenu = new wxMenu(); - viewMenu->AppendCheckItem(Menu_View_Updates, _("GUI Updates")); - viewMenu->Check(Menu_View_Updates, m_updates); - - wxMenu* actionMenu = new wxMenu(); - actionMenu->Append(Menu_Action_Shutdown, _("Shutdown")); - actionMenu->Append(Menu_Action_Startup, _("Startup")); - actionMenu->Append(Menu_Action_Command1, _("Command 1")); - actionMenu->Append(Menu_Action_Command2, _("Command 2")); - actionMenu->Append(Menu_Action_Command3, _("Command 3")); - actionMenu->Append(Menu_Action_Command4, _("Command 4")); - actionMenu->Append(Menu_Action_Command5, _("Command 5")); - actionMenu->Append(Menu_Action_Command6, _("Command 6")); - - m_outputMenu = new wxMenu(); - m_outputMenu->AppendCheckItem(Menu_Output_1, _("Output 1")); - m_outputMenu->AppendCheckItem(Menu_Output_2, _("Output 2")); - m_outputMenu->AppendCheckItem(Menu_Output_3, _("Output 3")); - m_outputMenu->AppendCheckItem(Menu_Output_4, _("Output 4")); - - wxMenu* helpMenu = new wxMenu(); - helpMenu->Append(wxID_ABOUT, _("About D-Star Repeater")); - - wxMenuBar* menuBar = new wxMenuBar(); - menuBar->Append(fileMenu, _("File")); - menuBar->Append(viewMenu, _("View")); - menuBar->Append(actionMenu, _("Action")); - menuBar->Append(m_outputMenu, _("Outputs")); - menuBar->Append(helpMenu, _("Help")); - - return menuBar; -} - -void CDStarRepeaterFrame::setLogging(bool logging) -{ - m_logging->Check(logging); -} - -void CDStarRepeaterFrame::onLogging(wxCommandEvent& event) -{ - ::wxGetApp().setLogging(event.IsChecked()); -} - -void CDStarRepeaterFrame::onQuit(wxCommandEvent&) -{ - Close(false); -} - -void CDStarRepeaterFrame::onClose(wxCloseEvent&) -{ - int x, y; - GetPosition(&x, &y); - if (x >= 0 && y >= 0) - ::wxGetApp().setPosition(x, y); - - Destroy(); -} - -void CDStarRepeaterFrame::onStatus(wxCommandEvent& event) -{ - switch (event.GetId()) { - case Menu_Action_Shutdown: - ((CDStarRepeaterApp *) wxTheApp)->shutdown(); - break; - case Menu_Action_Startup: - ((CDStarRepeaterApp *) wxTheApp)->startup(); - break; - } -} - -void CDStarRepeaterFrame::onActions(wxCommandEvent& event) -{ - wxThreadEvent evt(wxEVT_THREAD, wxEVT_THREAD_COMMAND); - - switch (event.GetId()) { - case Menu_Action_Command1: - evt.SetInt(0); - break; - case Menu_Action_Command2: - evt.SetInt(1); - break; - case Menu_Action_Command3: - evt.SetInt(2); - break; - case Menu_Action_Command4: - evt.SetInt(3); - break; - case Menu_Action_Command5: - evt.SetInt(4); - break; - case Menu_Action_Command6: - evt.SetInt(5); - break; - } - - wxTheApp->QueueEvent(evt.Clone()); -} - -void CDStarRepeaterFrame::onOutputs(wxCommandEvent&) -{ - bool out1 = m_outputMenu->IsChecked(Menu_Output_1); - bool out2 = m_outputMenu->IsChecked(Menu_Output_2); - bool out3 = m_outputMenu->IsChecked(Menu_Output_3); - bool out4 = m_outputMenu->IsChecked(Menu_Output_4); - - ::wxGetApp().setOutputs(out1, out2, out3, out4); -} - -void CDStarRepeaterFrame::onUpdates(wxCommandEvent& event) -{ - m_updates = event.IsChecked(); -} - -void CDStarRepeaterFrame::onAbout(wxCommandEvent&) -{ - wxAboutDialogInfo info; - info.AddDeveloper(wxT("Jonathan Naylor, G4KLX")); - info.SetCopyright(wxT("(C) 2013-2018 using GPL v2 or later")); - info.SetName(APPLICATION_NAME); - info.SetVersion(VERSION); - info.SetDescription(_("This program allows a computer with a suitable\nmodem to act as a D-Star repeater.")); - - ::wxAboutBox(info); -} - -void CDStarRepeaterFrame::onTimer(wxTimerEvent&) -{ - if (!m_updates) - return; - - CDStarRepeaterStatusData* status = ::wxGetApp().getStatus(); - if (status == NULL) - return; - - switch (status->getRxState()) { - case DSRXS_LISTENING: - m_rxState->SetLabel(_("Listening")); - break; - case DSRXS_PROCESS_DATA: - m_rxState->SetLabel(_("Process Data")); - break; - case DSRXS_PROCESS_SLOW_DATA: - m_rxState->SetLabel(_("Slow Data")); - break; - } - - switch (status->getRptState()) { - case DSRS_SHUTDOWN: - m_rptState->SetLabel(_("Shutdown")); - break; - case DSRS_LISTENING: - m_rptState->SetLabel(_("Listening")); - break; - case DSRS_VALID: - m_rptState->SetLabel(_("Valid")); - break; - case DSRS_VALID_WAIT: - case DSRS_INVALID_WAIT: - case DSRS_TIMEOUT_WAIT: - m_rptState->SetLabel(_("Wait")); - break; - case DSRS_INVALID: - m_rptState->SetLabel(_("Invalid")); - break; - case DSRS_TIMEOUT: - m_rptState->SetLabel(_("Timeout")); - break; - case DSRS_NETWORK: - m_rptState->SetLabel(_("Network")); - break; - } - - m_your->SetLabel(status->getYourCall()); - - wxString myCall = status->getMyCall1(); - wxString myCall2 = status->getMyCall2(); - if (!myCall2.IsEmpty() && !myCall2.IsSameAs(wxT(" "))) { - myCall.Append(wxT("/")); - myCall.Append(myCall2); - } - m_my->SetLabel(myCall); - - m_rpt1->SetLabel(status->getRptCall1()); - - m_rpt2->SetLabel(status->getRptCall2()); - - wxString text; - - text.Printf(wxT("%02X %02X %02X"), status->getFlag1(), status->getFlag2(), status->getFlag3()); - m_flags->SetLabel(text); - - text.Printf(wxT("%.1f%%"), status->getPercent()); - m_percent->SetLabel(text); - - bool tx = status->getTX(); - m_tx->SetLabel(tx ? _("On") : _("Off")); - - text.Printf(wxT("%u/%u"), status->getTimeoutTimer(), status->getTimeoutExpiry()); - m_timeout->SetLabel(text); - - text.Printf(wxT("%u/%u"), status->getBeaconTimer(), status->getBeaconExpiry()); - m_beacon->SetLabel(text); - - text.Printf(wxT("%u/%u"), status->getAnnounceTimer(), status->getAnnounceExpiry()); - m_announce->SetLabel(text); - - m_text->SetLabel(status->getText()); - m_status1->SetLabel(status->getStatus1()); - m_status2->SetLabel(status->getStatus2()); - m_status3->SetLabel(status->getStatus3()); - m_status4->SetLabel(status->getStatus4()); - m_status5->SetLabel(status->getStatus5()); - - // DVAP - if (m_squelch != NULL) { - if (tx) { - m_squelch->SetLabel(wxEmptyString); - m_signal->SetLabel(wxEmptyString); - } else { - bool squelch = status->getSquelch(); - m_squelch->SetLabel(squelch ? _("Open") : _("Closed")); - - text.Printf(wxT("%d dBm"), status->getSignal()); - m_signal->SetLabel(text); - } - } - - delete status; -} - -void CDStarRepeaterFrame::onLog(wxEvent& event) -{ - CLogEvent& logEvent = dynamic_cast(event); - - wxString text = logEvent.getText(); - - m_logLine1->SetLabel(m_logLine2->GetLabel()); - m_logLine2->SetLabel(m_logLine3->GetLabel()); - m_logLine3->SetLabel(text); -} - -void CDStarRepeaterFrame::showLog(const wxString& text) -{ - if (!m_updates) - return; - - CLogEvent event(text, LOG_EVENT); - - AddPendingEvent(event); -} - -void CDStarRepeaterFrame::setOutputs(bool out1, bool out2, bool out3, bool out4) -{ - m_outputMenu->Check(Menu_Output_1, out1); - m_outputMenu->Check(Menu_Output_2, out2); - m_outputMenu->Check(Menu_Output_3, out3); - m_outputMenu->Check(Menu_Output_4, out4); -} diff --git a/DStarRepeater/DStarRepeaterFrame.h b/DStarRepeater/DStarRepeaterFrame.h deleted file mode 100644 index 1cb25ca..0000000 --- a/DStarRepeater/DStarRepeaterFrame.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterFrame_H -#define DStarRepeaterFrame_H - -#include "DStarRepeaterDefs.h" - -#include - -class CDStarRepeaterFrame : public wxFrame { -public: - CDStarRepeaterFrame(const wxString& title, const wxString& type, const wxPoint& position, bool gui); - virtual ~CDStarRepeaterFrame(); - - virtual void setLogging(bool logging); - - virtual void onLogging(wxCommandEvent& event); - virtual void onQuit(wxCommandEvent& event); - virtual void onUpdates(wxCommandEvent& event); - virtual void onAbout(wxCommandEvent& event); - virtual void onClose(wxCloseEvent& event); - virtual void onStatus(wxCommandEvent& event); - virtual void onActions(wxCommandEvent& event); - virtual void onOutputs(wxCommandEvent& event); - virtual void onLog(wxEvent& event); - - virtual void onTimer(wxTimerEvent& event); - - virtual void showLog(const wxString& text); - - virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); - -private: - wxMenuItem* m_logging; - wxStaticText* m_rxState; - wxStaticText* m_rptState; - wxStaticText* m_tx; - wxStaticText* m_squelch; - wxStaticText* m_signal; - wxStaticText* m_your; - wxStaticText* m_my; - wxStaticText* m_rpt1; - wxStaticText* m_rpt2; - wxStaticText* m_flags; - wxStaticText* m_percent; - wxStaticText* m_timeout; - wxStaticText* m_beacon; - wxStaticText* m_announce; - wxStaticText* m_text; - wxStaticText* m_status1; - wxStaticText* m_status2; - wxStaticText* m_status3; - wxStaticText* m_status4; - wxStaticText* m_status5; - wxStaticText* m_logLine1; - wxStaticText* m_logLine2; - wxStaticText* m_logLine3; - wxTimer m_timer; - wxMenu* m_outputMenu; - bool m_updates; - - DECLARE_EVENT_TABLE() - - wxMenuBar* createMenuBar(); -}; - -#endif diff --git a/DStarRepeater/DStarRepeaterLogRedirect.cpp b/DStarRepeater/DStarRepeaterLogRedirect.cpp deleted file mode 100644 index 5b13f24..0000000 --- a/DStarRepeater/DStarRepeaterLogRedirect.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2002,2003,2009-2013,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 "DStarRepeaterLogRedirect.h" -#include "DStarRepeaterApp.h" - -CDStarRepeaterLogRedirect::CDStarRepeaterLogRedirect() : -wxLog() -{ -} - -CDStarRepeaterLogRedirect::~CDStarRepeaterLogRedirect() -{ -} - -void CDStarRepeaterLogRedirect::DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info) -{ - wxString letter; - - switch (level) { - case wxLOG_FatalError: letter = wxT("F"); break; - case wxLOG_Error: letter = wxT("E"); break; - case wxLOG_Warning: letter = wxT("W"); break; - case wxLOG_Info: letter = wxT("I"); break; - case wxLOG_Message: letter = wxT("M"); break; - case wxLOG_Status: letter = wxT("M"); break; - case wxLOG_Trace: letter = wxT("T"); break; - case wxLOG_Debug: letter = wxT("D"); break; - default: letter = wxT("U"); break; - } - - struct tm* tm = ::gmtime(&info.timestamp); - - wxString message; - message.Printf(wxT("%s: %04d-%02d-%02d %02d:%02d:%02d: %s\n"), letter.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, msg.c_str()); - - ::wxGetApp().showLog(message); - - if (level == wxLOG_FatalError) - ::abort(); -} diff --git a/DStarRepeater/DStarRepeaterLogRedirect.h b/DStarRepeater/DStarRepeaterLogRedirect.h deleted file mode 100644 index fa12afd..0000000 --- a/DStarRepeater/DStarRepeaterLogRedirect.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2002,2003,2009-2011,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 DStarRepeaterLogRedirect_H -#define DStarRepeaterLogRedirect_H - -#include -#include - -class CDStarRepeaterLogRedirect : public wxLog { -public: - CDStarRepeaterLogRedirect(); - virtual ~CDStarRepeaterLogRedirect(); - - virtual void DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info); - -private: -}; - -#endif diff --git a/DStarRepeater/DStarRepeaterRXThread.cpp b/DStarRepeater/DStarRepeaterRXThread.cpp index 20bcb67..3bde7ac 100644 --- a/DStarRepeater/DStarRepeaterRXThread.cpp +++ b/DStarRepeater/DStarRepeaterRXThread.cpp @@ -22,22 +22,22 @@ #include "DStarDefines.h" #include "HeaderData.h" #include "Version.h" +#include "Logger.h" #include "Utils.h" -const unsigned char DTMF_MASK[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U}; -const unsigned char DTMF_SIG[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; +#include -const unsigned int MAX_DATA_SYNC_BIT_ERRS = 2U; +using namespace std::chrono; -const unsigned int NETWORK_QUEUE_COUNT = 2U; +const unsigned int MAX_DATA_SYNC_BIT_ERRS = 2U; const unsigned int CYCLE_TIME = 9U; -CDStarRepeaterRXThread::CDStarRepeaterRXThread(const wxString& type) : +CDStarRepeaterRXThread::CDStarRepeaterRXThread(const std::string& type) : m_type(type), -m_modem(NULL), -m_protocolHandler(NULL), -m_rxHeader(NULL), +m_modem(nullptr), +m_protocolHandler(nullptr), +m_rxHeader(nullptr), m_radioSeqNo(0U), m_registerTimer(1000U), m_rptState(DSRS_LISTENING), @@ -60,35 +60,41 @@ m_lastAMBEErrors(0U) CDStarRepeaterRXThread::~CDStarRepeaterRXThread() { - delete m_rxHeader; + delete m_rxHeader; + delete m_modem; + delete m_protocolHandler; } -void *CDStarRepeaterRXThread::Entry() +// --------------------------------------------------------------------------- +// entry() — receive-only thread body. +// +// Requires modem and protocol handler before starting. Main loop (~9 ms): +// 1. receiveModem() — drain RF events; forward headers and AMBE to the network. +// 2. receiveNetwork() — drain and discard any inbound network packets (RX only). +// 3. Service the register timer (sends a keepalive to the gateway every 30 s). +// 4. MQTT status publication (1 s, if built with MQTT=1). +// --------------------------------------------------------------------------- +void CDStarRepeaterRXThread::entry() { // Wait here until we have the essentials to run - while (!m_killed && (m_modem == NULL || m_protocolHandler == NULL)) - ::wxMilliSleep(500UL); // 1/2 sec + while (!m_killed && (m_modem == nullptr || m_protocolHandler == nullptr)) + std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 1/2 sec if (m_killed) - return NULL; + return; m_registerTimer.start(10U); #if defined(MQTT) m_mqttStatusTimer.start(); #endif - wxString hardware = m_type; - int n = hardware.Find(wxT(' ')); - if (n != wxNOT_FOUND) - hardware = m_type.Left(n); + wxLogMessage("Starting the D-Star receiver thread"); - wxLogMessage(wxT("Starting the D-Star receiver thread")); - - wxStopWatch stopWatch; + auto stopWatch = steady_clock::now(); try { while (!m_killed) { - stopWatch.Start(); + stopWatch = steady_clock::now(); receiveModem(); @@ -103,7 +109,7 @@ void *CDStarRepeaterRXThread::Entry() #if defined(MQTT) // Publish status to MQTT every second if (m_mqttStatusTimer.hasExpired()) { - if (g_mqtt != NULL) { + if (g_mqtt != nullptr) { CDStarRepeaterStatusData* status = getStatus(); std::string json = status->toJSON(); g_mqtt->publish("status", json.c_str()); @@ -119,31 +125,31 @@ void *CDStarRepeaterRXThread::Entry() } #endif - unsigned long ms = stopWatch.Time(); + long long ms = duration_cast(steady_clock::now() - stopWatch).count(); if (ms < CYCLE_TIME) { - ::wxMilliSleep(CYCLE_TIME - ms); + std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_TIME - ms)); clock(CYCLE_TIME); } else { - clock(ms); + clock((unsigned int)ms); } } } catch (std::exception& e) { - wxString message(e.what(), wxConvLocal); - wxLogError(wxT("Exception raised - \"%s\""), message.c_str()); + wxLogError("Exception raised - \"%s\"", e.what()); } catch (...) { - wxLogError(wxT("Unknown exception raised")); + wxLogError("Unknown exception raised"); } - wxLogMessage(wxT("Stopping the D-Star receiver thread")); + wxLogMessage("Stopping the D-Star receiver thread"); m_modem->stop(); + delete m_modem; + m_modem = nullptr; m_protocolHandler->close(); delete m_protocolHandler; - - return NULL; + m_protocolHandler = nullptr; } void CDStarRepeaterRXThread::kill() @@ -151,20 +157,20 @@ void CDStarRepeaterRXThread::kill() m_killed = true; } -void CDStarRepeaterRXThread::setCallsign(const wxString&, const wxString&, DSTAR_MODE, ACK_TYPE, bool, bool, bool, bool) +void CDStarRepeaterRXThread::setCallsign(const std::string&, const std::string&, DSTAR_MODE, ACK_TYPE, bool, bool, bool, bool) { } void CDStarRepeaterRXThread::setProtocolHandler(CRepeaterProtocolHandler* handler, bool local) { - wxASSERT(handler != NULL); + assert(handler != nullptr); m_protocolHandler = handler; } void CDStarRepeaterRXThread::setModem(CModem* modem) { - wxASSERT(modem != NULL); + assert(modem != nullptr); m_modem = modem; } @@ -173,11 +179,11 @@ void CDStarRepeaterRXThread::setTimes(unsigned int, unsigned int) { } -void CDStarRepeaterRXThread::setBeacon(unsigned int, const wxString&, bool, TEXT_LANG) +void CDStarRepeaterRXThread::setBeacon(unsigned int, const std::string&, bool, TEXT_LANG) { } -void CDStarRepeaterRXThread::setAnnouncement(bool, unsigned int, const wxString&, const wxString&, const wxString&, const wxString&) +void CDStarRepeaterRXThread::setAnnouncement(bool, unsigned int, const std::string&, const std::string&, const std::string&, const std::string&) { } @@ -189,21 +195,15 @@ void CDStarRepeaterRXThread::setOutputs(bool, bool, bool, bool) { } -void CDStarRepeaterRXThread::setLogging(bool, const wxString&) +void CDStarRepeaterRXThread::setLogging(bool, const std::string&) { } -void CDStarRepeaterRXThread::setWhiteList(CCallsignList*) -{ -} +void CDStarRepeaterRXThread::setWhiteList(CCallsignList* list) { delete list; } -void CDStarRepeaterRXThread::setBlackList(CCallsignList*) -{ -} +void CDStarRepeaterRXThread::setBlackList(CCallsignList* list) { delete list; } -void CDStarRepeaterRXThread::setGreyList(CCallsignList*) -{ -} +void CDStarRepeaterRXThread::setGreyList(CCallsignList* list) { delete list; } void CDStarRepeaterRXThread::receiveModem() { @@ -258,9 +258,9 @@ void CDStarRepeaterRXThread::receiveModem() void CDStarRepeaterRXThread::receiveHeader(CHeaderData* header) { - wxASSERT(header != NULL); + assert(header != nullptr); - wxLogMessage(wxT("Radio header decoded - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Radio header decoded - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); bool res = processRadioHeader(header); if (res) { @@ -269,7 +269,7 @@ void CDStarRepeaterRXThread::receiveHeader(CHeaderData* header) setRadioState(DSRXS_PROCESS_DATA); } else { // This is a DD packet or some other problem - // wxLogMessage(wxT("Invalid header")); + // wxLogMessage("Invalid header"); } } @@ -282,11 +282,11 @@ void CDStarRepeaterRXThread::receiveSlowData(unsigned char* data, unsigned int) // The data sync has been seen, a fuzzy match is used, two bit errors or less if (errs <= MAX_DATA_SYNC_BIT_ERRS) { - // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + // wxLogMessage("Found data sync at frame %u, errs: %u", m_radioSeqNo, errs); m_radioSeqNo = 0U; m_slowDataDecoder.sync(); } else if (m_radioSeqNo == 20U) { - // wxLogMessage(wxT("Assuming data sync")); + // wxLogMessage("Assuming data sync"); m_radioSeqNo = 0U; m_slowDataDecoder.sync(); } else { @@ -294,20 +294,18 @@ void CDStarRepeaterRXThread::receiveSlowData(unsigned char* data, unsigned int) m_slowDataDecoder.addData(data + VOICE_FRAME_LENGTH_BYTES); CHeaderData* header = m_slowDataDecoder.getHeaderData(); - if (header == NULL) + if (header == nullptr) return; - wxLogMessage(wxT("Radio header from slow data - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X BER: 0%%"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Radio header from slow data - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X BER: 0%%", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); - if (header != NULL) { - bool res = processRadioHeader(header); - if (res) { - // A valid header and is a DV packet, go to normal data relaying - setRadioState(DSRXS_PROCESS_DATA); - } else { - // This is a DD packet or some other problem - // wxLogMessage(wxT("Invalid header")); - } + bool res = processRadioHeader(header); + if (res) { + // A valid header and is a DV packet, go to normal data relaying + setRadioState(DSRXS_PROCESS_DATA); + } else { + // This is a DD packet or some other problem + // wxLogMessage("Invalid header"); } } } @@ -321,11 +319,11 @@ void CDStarRepeaterRXThread::receiveRadioData(unsigned char* data, unsigned int) // The data sync has been seen, a fuzzy match is used, two bit errors or less if (errs <= MAX_DATA_SYNC_BIT_ERRS) { - // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + // wxLogMessage("Found data sync at frame %u, errs: %u", m_radioSeqNo, errs); m_radioSeqNo = 0U; processRadioFrame(data, FRAME_SYNC); } else if (m_radioSeqNo == 20U) { - // wxLogMessage(wxT("Regenerating data sync")); + // wxLogMessage("Regenerating data sync"); m_radioSeqNo = 0U; processRadioFrame(data, FRAME_SYNC); } else { @@ -334,6 +332,7 @@ void CDStarRepeaterRXThread::receiveRadioData(unsigned char* data, unsigned int) } } +// Drain and discard all inbound network packets — this thread cannot transmit. void CDStarRepeaterRXThread::receiveNetwork() { NETWORK_TYPE type; @@ -345,7 +344,7 @@ void CDStarRepeaterRXThread::receiveNetwork() void CDStarRepeaterRXThread::setRadioState(DSTAR_RX_STATE state) { - // This is the too state + // This is the to state switch (state) { case DSRXS_LISTENING: m_rxState = DSRXS_LISTENING; @@ -371,11 +370,11 @@ void CDStarRepeaterRXThread::setRadioState(DSTAR_RX_STATE state) bool CDStarRepeaterRXThread::processRadioHeader(CHeaderData* header) { - wxASSERT(header != NULL); + assert(header != nullptr); // We don't handle DD data packets if (header->isDataPacket()) { - wxLogMessage(wxT("Received a DD packet, ignoring")); + wxLogMessage("Received a DD packet, ignoring"); delete header; return false; } @@ -436,7 +435,7 @@ void CDStarRepeaterRXThread::processRadioFrame(unsigned char* data, FRAME_TYPE t void CDStarRepeaterRXThread::endOfRadioData() { - wxLogMessage(wxT("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%"), m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); + wxLogMessage("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%", m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); m_rptState = DSRS_LISTENING; @@ -455,17 +454,17 @@ CDStarRepeaterStatusData* CDStarRepeaterRXThread::getStatus() CDStarRepeaterStatusData* status; if (m_rptState == DSRS_SHUTDOWN || m_rptState == DSRS_LISTENING) - status = new CDStarRepeaterStatusData(wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, - wxEmptyString, 0x00, 0x00, 0x00, false, m_rxState, m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, 0.0F, - wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString); + status = new CDStarRepeaterStatusData(std::string(), std::string(), std::string(), std::string(), + std::string(), 0x00, 0x00, 0x00, false, m_rxState, m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, 0.0F, + std::string(), std::string(), std::string(), std::string(), std::string(), std::string()); else status = new CDStarRepeaterStatusData(m_rxHeader->getMyCall1(), m_rxHeader->getMyCall2(), m_rxHeader->getYourCall(), m_rxHeader->getRptCall1(), m_rxHeader->getRptCall2(), m_rxHeader->getFlag1(), m_rxHeader->getFlag2(), m_rxHeader->getFlag3(), false, m_rxState, - m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, (errors * 100.0F) / bits, wxEmptyString, wxEmptyString, - wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString); + m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, (errors * 100.0F) / bits, std::string(), std::string(), + std::string(), std::string(), std::string(), std::string()); - if (m_type.IsSameAs(wxT("DVAP")) && m_modem != NULL) { + if (m_type == "DVAP" && m_modem != nullptr) { CDVAPController* dvap = static_cast(m_modem); bool squelch = dvap->getSquelch(); int signal = dvap->getSignal(); @@ -491,30 +490,6 @@ void CDStarRepeaterRXThread::startup() { } -void CDStarRepeaterRXThread::command1() -{ -} - -void CDStarRepeaterRXThread::command2() -{ -} - -void CDStarRepeaterRXThread::command3() -{ -} - -void CDStarRepeaterRXThread::command4() -{ -} - -void CDStarRepeaterRXThread::command5() -{ -} - -void CDStarRepeaterRXThread::command6() -{ -} - unsigned int CDStarRepeaterRXThread::countBits(unsigned char byte) { unsigned int bits = 0U; diff --git a/DStarRepeater/DStarRepeaterRXThread.h b/DStarRepeater/DStarRepeaterRXThread.h index f19acb3..1c129ed 100644 --- a/DStarRepeater/DStarRepeaterRXThread.h +++ b/DStarRepeater/DStarRepeaterRXThread.h @@ -28,23 +28,33 @@ #include "MQTTPublisher.h" #endif -#include - +#include "StdCompat.h" +#include + +// CDStarRepeaterRXThread — receive-only repeater thread (MODE_RXONLY). +// +// Listens for RF transmissions, validates the header, counts AMBE BER, and +// forwards every frame to the gateway via the protocol handler. There is no +// local transmit queue, no ack, no beacon, and no controller PTT. The gateway +// link is required (a protocol handler must be set before entry() will run). +// +// Compared with TRXThread the state machine is trivial: LISTENING ↔ VALID +// with no timeout, ack-wait, or network states. class CDStarRepeaterRXThread : public IDStarRepeaterThread { public: - CDStarRepeaterRXThread(const wxString& type); + CDStarRepeaterRXThread(const std::string& type); virtual ~CDStarRepeaterRXThread(); - virtual void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); + virtual void setCallsign(const std::string& callsign, const std::string& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); virtual void setProtocolHandler(CRepeaterProtocolHandler* handler, bool local); virtual void setModem(CModem* modem); virtual void setController(CExternalController* controller, unsigned int activeHangTime); virtual void setTimes(unsigned int timeout, unsigned int ackTime); - virtual void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language); - virtual void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); + virtual void setBeacon(unsigned int time, const std::string& text, bool voice, TEXT_LANG language); + virtual void setAnnouncement(bool enabled, unsigned int time, const std::string& recordRPT1, const std::string& recordRPT2, const std::string& deleteRPT1, const std::string& deleteRPT2); virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); - virtual void setLogging(bool logging, const wxString& dir); + virtual void setLogging(bool logging, const std::string& dir); virtual void setWhiteList(CCallsignList* list); virtual void setBlackList(CCallsignList* list); virtual void setGreyList(CCallsignList* list); @@ -52,21 +62,14 @@ public: virtual void shutdown(); virtual void startup(); - virtual void command1(); - virtual void command2(); - virtual void command3(); - virtual void command4(); - virtual void command5(); - virtual void command6(); - virtual CDStarRepeaterStatusData* getStatus(); virtual void kill(); - virtual void *Entry(); + virtual void entry(); private: - wxString m_type; + std::string m_type; CModem* m_modem; CRepeaterProtocolHandler* m_protocolHandler; CHeaderData* m_rxHeader; @@ -75,7 +78,7 @@ private: DSTAR_RPT_STATE m_rptState; DSTAR_RX_STATE m_rxState; CSlowDataDecoder m_slowDataDecoder; - bool m_killed; + std::atomic m_killed; CAMBEFEC m_ambe; unsigned int m_ambeFrames; unsigned int m_ambeSilence; diff --git a/DStarRepeater/DStarRepeaterStatusData.cpp b/DStarRepeater/DStarRepeaterStatusData.cpp index 003e08f..6e0c029 100644 --- a/DStarRepeater/DStarRepeaterStatusData.cpp +++ b/DStarRepeater/DStarRepeaterStatusData.cpp @@ -20,19 +20,39 @@ #if defined(MQTT) #include +#include + +// Escape a string for safe embedding in a JSON value. +// Identical to the copy in MQTTPublisher.h — kept local to avoid a header +// dependency between DStarRepeater/ and Common/. +static std::string jsonEscape(const std::string& s) +{ + std::string out; + out.reserve(s.size()); + for (char c : s) { + if (c == '"') out += "\\\""; + else if (c == '\\') out += "\\\\"; + else if (c == '\n') out += "\\n"; + else if (c == '\r') out += "\\r"; + else if (c == '\t') out += "\\t"; + else if (c >= 0x20) out += c; + // drop other non-printable control characters + } + return out; +} #endif -CDStarRepeaterStatusData::CDStarRepeaterStatusData(const wxString& myCall1, const wxString& myCall2, - const wxString& yourCall, const wxString& rptCall1, - const wxString& rptCall2, unsigned char flag1, +CDStarRepeaterStatusData::CDStarRepeaterStatusData(const std::string& myCall1, const std::string& myCall2, + const std::string& yourCall, const std::string& rptCall1, + const std::string& rptCall2, unsigned char flag1, unsigned char flag2, unsigned char flag3, bool tx, DSTAR_RX_STATE rxState, DSTAR_RPT_STATE rptState, unsigned int timeoutTimer, unsigned int timeoutExpiry, unsigned int beaconTimer, unsigned int beaconExpiry, unsigned int announceTimer, unsigned int announceExpiry, - float percent, const wxString& text, const wxString& status1, - const wxString& status2, const wxString& status3, - const wxString& status4, const wxString& status5) : + float percent, const std::string& text, const std::string& status1, + const std::string& status2, const std::string& status3, + const std::string& status4, const std::string& status5) : m_myCall1(myCall1), m_myCall2(myCall2), m_yourCall(yourCall), @@ -72,27 +92,27 @@ void CDStarRepeaterStatusData::setDVAP(bool squelch, int signal) m_signal = signal; } -wxString CDStarRepeaterStatusData::getMyCall1() const +std::string CDStarRepeaterStatusData::getMyCall1() const { return m_myCall1; } -wxString CDStarRepeaterStatusData::getMyCall2() const +std::string CDStarRepeaterStatusData::getMyCall2() const { return m_myCall2; } -wxString CDStarRepeaterStatusData::getYourCall() const +std::string CDStarRepeaterStatusData::getYourCall() const { return m_yourCall; } -wxString CDStarRepeaterStatusData::getRptCall1() const +std::string CDStarRepeaterStatusData::getRptCall1() const { return m_rptCall1; } -wxString CDStarRepeaterStatusData::getRptCall2() const +std::string CDStarRepeaterStatusData::getRptCall2() const { return m_rptCall2; } @@ -172,36 +192,38 @@ float CDStarRepeaterStatusData::getPercent() const return m_percent; } -wxString CDStarRepeaterStatusData::getText() const +std::string CDStarRepeaterStatusData::getText() const { return m_text; } -wxString CDStarRepeaterStatusData::getStatus1() const +std::string CDStarRepeaterStatusData::getStatus1() const { return m_status1; } -wxString CDStarRepeaterStatusData::getStatus2() const +std::string CDStarRepeaterStatusData::getStatus2() const { return m_status2; } -wxString CDStarRepeaterStatusData::getStatus3() const +std::string CDStarRepeaterStatusData::getStatus3() const { return m_status3; } -wxString CDStarRepeaterStatusData::getStatus4() const +std::string CDStarRepeaterStatusData::getStatus4() const { return m_status4; } -wxString CDStarRepeaterStatusData::getStatus5() const +std::string CDStarRepeaterStatusData::getStatus5() const { return m_status5; } +// toJSON() serialises the snapshot to a JSON string for MQTT publication. +// The output format is consumed by Display-Driver and similar monitoring tools. #if defined(MQTT) static const char* rptStateToString(DSTAR_RPT_STATE state) { @@ -231,7 +253,22 @@ static const char* rxStateToString(DSTAR_RX_STATE state) std::string CDStarRepeaterStatusData::toJSON() const { - char buffer[1024]; + // Escape every field that originates from over-the-air data or gateway + // text to prevent JSON injection. The state-machine strings and the + // boolean are produced by our own code and need no escaping. + const std::string mc1 = jsonEscape(m_myCall1); + const std::string mc2 = jsonEscape(m_myCall2); + const std::string yc = jsonEscape(m_yourCall); + const std::string rc1 = jsonEscape(m_rptCall1); + const std::string rc2 = jsonEscape(m_rptCall2); + const std::string txt = jsonEscape(m_text); + const std::string s1 = jsonEscape(m_status1); + const std::string s2 = jsonEscape(m_status2); + const std::string s3 = jsonEscape(m_status3); + const std::string s4 = jsonEscape(m_status4); + const std::string s5 = jsonEscape(m_status5); + + char buffer[2048]; ::snprintf(buffer, sizeof(buffer), "{\"myCall1\":\"%s\",\"myCall2\":\"%s\"," "\"yourCall\":\"%s\",\"rptCall1\":\"%s\",\"rptCall2\":\"%s\"," @@ -240,21 +277,21 @@ std::string CDStarRepeaterStatusData::toJSON() const "\"text\":\"%s\"," "\"status1\":\"%s\",\"status2\":\"%s\",\"status3\":\"%s\"," "\"status4\":\"%s\",\"status5\":\"%s\"}", - (const char*)m_myCall1.mb_str(), - (const char*)m_myCall2.mb_str(), - (const char*)m_yourCall.mb_str(), - (const char*)m_rptCall1.mb_str(), - (const char*)m_rptCall2.mb_str(), + mc1.c_str(), + mc2.c_str(), + yc.c_str(), + rc1.c_str(), + rc2.c_str(), m_tx ? "true" : "false", rxStateToString(m_rxState), rptStateToString(m_rptState), m_percent, - (const char*)m_text.mb_str(), - (const char*)m_status1.mb_str(), - (const char*)m_status2.mb_str(), - (const char*)m_status3.mb_str(), - (const char*)m_status4.mb_str(), - (const char*)m_status5.mb_str()); + txt.c_str(), + s1.c_str(), + s2.c_str(), + s3.c_str(), + s4.c_str(), + s5.c_str()); return std::string(buffer); } diff --git a/DStarRepeater/DStarRepeaterStatusData.h b/DStarRepeater/DStarRepeaterStatusData.h index 9e8864d..1b02545 100644 --- a/DStarRepeater/DStarRepeaterStatusData.h +++ b/DStarRepeater/DStarRepeaterStatusData.h @@ -22,31 +22,40 @@ #include "DStarRepeaterDefs.h" #include "DStarDefines.h" -#include - -#if defined(MQTT) -#include -#endif - +#include "StdCompat.h" + +// CDStarRepeaterStatusData — an immutable snapshot of repeater state captured +// at a single point in time. +// +// Created by each thread's getStatus() method and consumed by: +// - The GUI display panel (polling via a timer). +// - The MQTT publisher (toJSON(), published every 1 s to {name}/status). +// +// Contains the D-Star header fields of the current transmission, the TX flag, +// both state-machine states, timer values for the timeout/beacon/announcement +// countdown displays, the current BER or loss percentage, the gateway-supplied +// ack text, and the five status strings. +// +// For DVAP modems, squelch and signal-strength fields are added via setDVAP(). class CDStarRepeaterStatusData { public: - CDStarRepeaterStatusData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall, - const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1, + CDStarRepeaterStatusData(const std::string& myCall1, const std::string& myCall2, const std::string& yourCall, + const std::string& rptCall1, const std::string& rptCall2, unsigned char flag1, unsigned char flag2, unsigned char flag3, bool tx, DSTAR_RX_STATE rxState, DSTAR_RPT_STATE rptState, unsigned int timeoutTimer, unsigned int timeoutExpiry, unsigned int beaconTimer, unsigned int beaconExpiry, unsigned int announceTimer, - unsigned int announceExpiry, float percent, const wxString& text, - const wxString& status1, const wxString& status2, const wxString& status3, - const wxString& status4, const wxString& status5); + unsigned int announceExpiry, float percent, const std::string& text, + const std::string& status1, const std::string& status2, const std::string& status3, + const std::string& status4, const std::string& status5); ~CDStarRepeaterStatusData(); void setDVAP(bool squelch, int signal); - wxString getMyCall1() const; - wxString getMyCall2() const; - wxString getYourCall() const; - wxString getRptCall1() const; - wxString getRptCall2() const; + std::string getMyCall1() const; + std::string getMyCall2() const; + std::string getYourCall() const; + std::string getRptCall1() const; + std::string getRptCall2() const; unsigned char getFlag1() const; unsigned char getFlag2() const; unsigned char getFlag3() const; @@ -69,23 +78,23 @@ public: float getPercent() const; - wxString getText() const; - wxString getStatus1() const; - wxString getStatus2() const; - wxString getStatus3() const; - wxString getStatus4() const; - wxString getStatus5() const; + std::string getText() const; + std::string getStatus1() const; + std::string getStatus2() const; + std::string getStatus3() const; + std::string getStatus4() const; + std::string getStatus5() const; #if defined(MQTT) std::string toJSON() const; #endif private: - wxString m_myCall1; - wxString m_myCall2; - wxString m_yourCall; - wxString m_rptCall1; - wxString m_rptCall2; + std::string m_myCall1; + std::string m_myCall2; + std::string m_yourCall; + std::string m_rptCall1; + std::string m_rptCall2; unsigned char m_flag1; unsigned char m_flag2; unsigned char m_flag3; @@ -99,12 +108,12 @@ private: unsigned int m_announceTimer; unsigned int m_announceExpiry; float m_percent; - wxString m_text; - wxString m_status1; - wxString m_status2; - wxString m_status3; - wxString m_status4; - wxString m_status5; + std::string m_text; + std::string m_status1; + std::string m_status2; + std::string m_status3; + std::string m_status4; + std::string m_status5; // DVAP bool m_squelch; diff --git a/DStarRepeater/DStarRepeaterTRXThread.cpp b/DStarRepeater/DStarRepeaterTRXThread.cpp index 1a7183c..4203e30 100644 --- a/DStarRepeater/DStarRepeaterTRXThread.cpp +++ b/DStarRepeater/DStarRepeaterTRXThread.cpp @@ -18,41 +18,60 @@ #include "DStarRepeaterStatusData.h" #include "DStarRepeaterTRXThread.h" -#include "DStarRepeaterApp.h" #include "DVAPController.h" #include "DStarDefines.h" #include "HeaderData.h" #include "Version.h" +#include "Logger.h" +#include +#include +#include + +using namespace std::chrono; + +// Bit-mask and expected signature for AMBE frames that contain DTMF tones. +// Applied in blankDTMF() to detect and mute DTMF before retransmission. const unsigned char DTMF_MASK[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U}; const unsigned char DTMF_SIG[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; +// Fuzzy sync detection: allow up to 2 bit errors when matching the data-sync pattern. const unsigned int MAX_DATA_SYNC_BIT_ERRS = 2U; +// Double-buffer the network output queue so a new transmission can be staged +// while the previous one is still being transmitted. const unsigned int NETWORK_QUEUE_COUNT = 2U; +// If a gap is 2 frames or fewer, repeat the last frame rather than inserting silence. const unsigned int SILENCE_THRESHOLD = 2U; +// Target loop period in milliseconds. The main loop sleeps for any remaining +// time after its work is done, then clocks all timers with the actual elapsed ms. const unsigned int CYCLE_TIME = 9U; -CDStarRepeaterTRXThread::CDStarRepeaterTRXThread(const wxString& type) : +// --------------------------------------------------------------------------- +// Constructor — initialise all members to safe defaults. +// The two output queues (local, radio) are sized for 1 s of audio. +// The network queue array is heap-allocated as a double buffer (4 s each). +// Both state machines start in LISTENING. +// --------------------------------------------------------------------------- +CDStarRepeaterTRXThread::CDStarRepeaterTRXThread(const std::string& type) : m_type(type), -m_modem(NULL), -m_protocolHandler(NULL), -m_controller(NULL), -m_stopped(true), +m_modem(nullptr), +m_protocolHandler(nullptr), +m_controller(nullptr), m_rptCallsign(), m_gwyCallsign(), -m_beacon(NULL), -m_announcement(NULL), +m_beacon(nullptr), +m_announcement(nullptr), m_recordRPT1(), m_recordRPT2(), m_deleteRPT1(), m_deleteRPT2(), -m_rxHeader(NULL), +m_rxHeader(nullptr), m_localQueue((DV_FRAME_LENGTH_BYTES + 2U) * 50U, LOCAL_RUN_FRAME_COUNT), // 1s worth of data m_radioQueue((DV_FRAME_LENGTH_BYTES + 2U) * 50U, RADIO_RUN_FRAME_COUNT), // 1s worth of data -m_networkQueue(NULL), +m_networkQueue(nullptr), m_writeNum(0U), m_readNum(0U), m_radioSeqNo(0U), @@ -93,8 +112,8 @@ m_controlStartup(), m_activeHangTimer(1000U), m_shutdown(false), m_disable(false), -m_logging(NULL), -m_lastData(NULL), +m_logging(nullptr), +m_lastData(nullptr), m_ambe(), m_ambeFrames(0U), m_ambeSilence(0U), @@ -106,14 +125,14 @@ m_ackText(), m_tempAckText(), m_linkStatus(LS_NONE), m_reflector(), -m_regEx(wxT("^[A-Z0-9]{1}[A-Z0-9]{0,1}[0-9]{1,2}[A-Z]{1,4} {0,4}[ A-Z]{1}$")), +m_regEx("^[A-Z0-9]{1}[A-Z0-9]{0,1}[0-9]{1,2}[A-Z]{1,4} {0,4}[ A-Z]{1}$"), m_headerTime(), m_packetTime(), m_packetCount(0U), m_packetSilence(0U), -m_whiteList(NULL), -m_blackList(NULL), -m_greyList(NULL), +m_whiteList(nullptr), +m_blackList(nullptr), +m_greyList(nullptr), m_blocked(false), m_busyData(false), m_blanking(true), @@ -126,7 +145,7 @@ m_deleting(false) for(int i = 0; i < 5; ++i) m_statusAnnounceTimer[i] = CTimer(1000U, 3U); - m_statusText.Add("", 5); + m_statusText.resize(5); m_networkQueue = new COutputQueue*[NETWORK_QUEUE_COUNT]; for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) @@ -145,18 +164,43 @@ CDStarRepeaterTRXThread::~CDStarRepeaterTRXThread() delete[] m_networkQueue; delete[] m_lastData; delete m_rxHeader; + delete m_modem; + delete m_controller; + delete m_protocolHandler; + delete m_beacon; + delete m_announcement; + delete m_whiteList; + delete m_blackList; + delete m_greyList; + delete m_logging; } -void *CDStarRepeaterTRXThread::Entry() +// --------------------------------------------------------------------------- +// entry() — the repeater thread body. +// +// Startup barrier: spin until the modem, controller, and callsign are all +// configured (set by the main thread via the set*() methods). +// +// Main loop (~9 ms cycle): +// 1. Refresh modem space/TX state every 100 ms. +// 2. receiveModem() — drain the modem event queue; feed the state machine. +// 3. receiveNetwork() — drain the gateway event queue; fill the network output queue. +// 4. repeaterStateMachine() — handle timer-driven state transitions (timeout, watchdog, ack delay). +// 5. Service periodic timers: gateway poll, beacon, announcement, status announces, heartbeat, MQTT. +// 6. Drive the active-indicator output and handle shutdown/startup transitions. +// 7. Feed the highest-priority queue to the modem (radio > local > network). +// 8. Sleep for the remainder of the 9 ms cycle and clock all timers. +// +// Shutdown: on kill(), the loop exits and all subsystems are torn down in order. +// --------------------------------------------------------------------------- +void CDStarRepeaterTRXThread::entry() { // Wait here until we have the essentials to run - while (!m_killed && (m_modem == NULL || m_controller == NULL || m_rptCallsign.IsEmpty() || m_rptCallsign.IsSameAs(wxT(" ")))) - ::wxMilliSleep(500UL); // 1/2 sec + while (!m_killed && (m_modem == nullptr || m_controller == nullptr || m_rptCallsign.empty() || m_rptCallsign == " ")) + std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 1/2 sec if (m_killed) - return NULL; - - m_stopped = false; + return; m_beaconTimer.start(); m_announcementTimer.start(); @@ -168,31 +212,38 @@ void *CDStarRepeaterTRXThread::Entry() m_mqttStatusTimer.start(); #endif - if (m_protocolHandler != NULL) + if (m_protocolHandler != nullptr) m_pollTimer.start(); - wxString hardware = m_type; - int n = hardware.Find(wxT(' ')); - if (n != wxNOT_FOUND) - hardware = m_type.Left(n); + std::string hardware = m_type; + size_t n = hardware.find(' '); + if (n != std::string::npos) + hardware = m_type.substr(0, n); + + char pollBuf[128]; + ::snprintf(pollBuf, sizeof(pollBuf), "linux_%s-%s", hardware.c_str(), VERSION.c_str()); + std::string pollText(pollBuf); + // Replace spaces with dashes + { + size_t pos = 0; + while ((pos = pollText.find(' ', pos)) != std::string::npos) { + pollText.replace(pos, 1, "-"); + pos += 1; + } + } + // Make lower case + for (char& c : pollText) + c = (char)tolower((unsigned char)c); - wxString pollText; -#if defined(__WINDOWS__) - pollText.Printf(wxT("win_%s-%s"), hardware.c_str(), VERSION.c_str()); -#else - pollText.Printf(wxT("linux_%s-%s"), hardware.c_str(), VERSION.c_str()); -#endif - pollText.Replace(wxT(" "), wxT("-")); - pollText.MakeLower(); - wxLogMessage(wxT("Poll text set to \"%s\""), pollText.c_str()); + wxLogMessage("Poll text set to \"%s\"", pollText.c_str()); - wxLogMessage(wxT("Starting the D-Star repeater thread")); + wxLogMessage("Starting the D-Star repeater thread"); - wxStopWatch stopWatch; + auto stopWatch = steady_clock::now(); try { while (!m_killed) { - stopWatch.Start(); + stopWatch = steady_clock::now(); if (m_statusTimer.hasExpired() || m_space == 0U) { m_space = m_modem->getSpace(); @@ -224,6 +275,8 @@ void *CDStarRepeaterTRXThread::Entry() m_announcementTimer.start(); } + // A status announce timer fires 3 s after a DTMF trigger. + // Only transmit if the repeater is back to LISTENING by then. for(int i = 0; i < 5; ++ i) { if(m_statusAnnounceTimer[i].isRunning() && m_statusAnnounceTimer[i].hasExpired()) { @@ -242,17 +295,25 @@ void *CDStarRepeaterTRXThread::Entry() #if defined(MQTT) // Publish status to MQTT every second if (m_mqttStatusTimer.hasExpired()) { - if (g_mqtt != NULL) { + if (g_mqtt != nullptr) { CDStarRepeaterStatusData* status = getStatus(); std::string json = status->toJSON(); g_mqtt->publish("status", json.c_str()); delete status; - // Publish BER for Display-Driver during active RF + // Publish BER and RSSI for Display-Driver during active RF if (m_rptState == DSRS_VALID && m_ambeBits > 0U) { float ber = float(m_ambeErrors * 100U) / float(m_ambeBits); mqttPublishBER(ber); } + + // Publish DVAP signal strength as RSSI during active RF. + // No m_ambeBits guard needed — the DVAP continuously updates + // m_signal from hardware regardless of voice frame arrival. + if (m_rptState == DSRS_VALID && m_type == "DVAP" && m_modem != nullptr) { + CDVAPController* dvap = static_cast(m_modem); + mqttPublishRSSI(dvap->getSignal()); + } } m_mqttStatusTimer.start(); } @@ -295,7 +356,7 @@ void *CDStarRepeaterTRXThread::Entry() m_beaconTimer.start(); m_announcementTimer.start(); m_rptState = DSRS_LISTENING; - if (m_protocolHandler != NULL) // Tell the protocol handler + if (m_protocolHandler != nullptr) // Tell the protocol handler m_protocolHandler->reset(); } } @@ -315,50 +376,57 @@ void *CDStarRepeaterTRXThread::Entry() m_controller->setRadioTransmit(m_tx); - unsigned long ms = stopWatch.Time(); + long long ms = duration_cast(steady_clock::now() - stopWatch).count(); if (ms < CYCLE_TIME) { - ::wxMilliSleep(CYCLE_TIME - ms); + std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_TIME - ms)); clock(CYCLE_TIME); } else { - clock(ms); + clock((unsigned int)ms); } } } catch (std::exception& e) { - wxString message(e.what(), wxConvLocal); - wxLogError(wxT("Exception raised - \"%s\""), message.c_str()); + wxLogError("Exception raised - \"%s\"", e.what()); } catch (...) { - wxLogError(wxT("Unknown exception raised")); + wxLogError("Unknown exception raised"); } - wxLogMessage(wxT("Stopping the D-Star repeater thread")); + wxLogMessage("Stopping the D-Star repeater thread"); m_modem->stop(); + delete m_modem; + m_modem = nullptr; - if (m_logging != NULL) { + if (m_logging != nullptr) { m_logging->close(); delete m_logging; + m_logging = nullptr; } delete m_beacon; + m_beacon = nullptr; delete m_announcement; + m_announcement = nullptr; delete m_whiteList; + m_whiteList = nullptr; delete m_blackList; + m_blackList = nullptr; delete m_greyList; + m_greyList = nullptr; m_controller->setActive(false); m_controller->setRadioTransmit(false); m_controller->close(); delete m_controller; + m_controller = nullptr; - if (m_protocolHandler != NULL) { + if (m_protocolHandler != nullptr) { m_protocolHandler->close(); delete m_protocolHandler; + m_protocolHandler = nullptr; } - - return NULL; } void CDStarRepeaterTRXThread::kill() @@ -366,19 +434,19 @@ void CDStarRepeaterTRXThread::kill() m_killed = true; } -void CDStarRepeaterTRXThread::setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply) +void CDStarRepeaterTRXThread::setCallsign(const std::string& callsign, const std::string& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply) { // Pad the callsign up to eight characters m_rptCallsign = callsign; - m_rptCallsign.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + m_rptCallsign.resize(LONG_CALLSIGN_LENGTH, ' '); - if (gateway.IsEmpty()) { + if (gateway.empty()) { m_gwyCallsign = callsign; - m_gwyCallsign.resize(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); - m_gwyCallsign.Append(wxT("G")); + m_gwyCallsign.resize(LONG_CALLSIGN_LENGTH - 1U, ' '); + m_gwyCallsign += "G"; } else { m_gwyCallsign = gateway; - m_gwyCallsign.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + m_gwyCallsign.resize(LONG_CALLSIGN_LENGTH, ' '); } m_mode = mode; @@ -391,14 +459,14 @@ void CDStarRepeaterTRXThread::setCallsign(const wxString& callsign, const wxStri void CDStarRepeaterTRXThread::setProtocolHandler(CRepeaterProtocolHandler* handler, bool local) { - wxASSERT(handler != NULL); + assert(handler != nullptr); m_protocolHandler = handler; } void CDStarRepeaterTRXThread::setModem(CModem* modem) { - wxASSERT(modem != NULL); + assert(modem != nullptr); m_modem = modem; } @@ -409,7 +477,7 @@ void CDStarRepeaterTRXThread::setTimes(unsigned int timeout, unsigned int ackTim m_ackTimer.setTimeout(0U, ackTime); } -void CDStarRepeaterTRXThread::setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language) +void CDStarRepeaterTRXThread::setBeacon(unsigned int time, const std::string& text, bool voice, TEXT_LANG language) { m_beaconTimer.setTimeout(time); @@ -417,7 +485,7 @@ void CDStarRepeaterTRXThread::setBeacon(unsigned int time, const wxString& text, m_beacon = new CBeaconUnit(this, m_rptCallsign, text, voice, language); } -void CDStarRepeaterTRXThread::setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2) +void CDStarRepeaterTRXThread::setAnnouncement(bool enabled, unsigned int time, const std::string& recordRPT1, const std::string& recordRPT2, const std::string& deleteRPT1, const std::string& deleteRPT2) { if (enabled && time > 0U) { m_announcement = new CAnnouncementUnit(this, m_rptCallsign); @@ -429,31 +497,31 @@ void CDStarRepeaterTRXThread::setAnnouncement(bool enabled, unsigned int time, c m_deleteRPT1 = deleteRPT1; m_deleteRPT2 = deleteRPT2; - m_recordRPT1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_recordRPT2.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_deleteRPT1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_deleteRPT2.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_recordRPT1.append(LONG_CALLSIGN_LENGTH, ' '); + m_recordRPT2.append(LONG_CALLSIGN_LENGTH, ' '); + m_deleteRPT1.append(LONG_CALLSIGN_LENGTH, ' '); + m_deleteRPT2.append(LONG_CALLSIGN_LENGTH, ' '); - m_recordRPT1.Truncate(LONG_CALLSIGN_LENGTH); - m_recordRPT2.Truncate(LONG_CALLSIGN_LENGTH); - m_deleteRPT1.Truncate(LONG_CALLSIGN_LENGTH); - m_deleteRPT2.Truncate(LONG_CALLSIGN_LENGTH); + m_recordRPT1.resize(LONG_CALLSIGN_LENGTH); + m_recordRPT2.resize(LONG_CALLSIGN_LENGTH); + m_deleteRPT1.resize(LONG_CALLSIGN_LENGTH); + m_deleteRPT2.resize(LONG_CALLSIGN_LENGTH); } } void CDStarRepeaterTRXThread::setController(CExternalController* controller, unsigned int activeHangTime) { - wxASSERT(controller != NULL); + assert(controller != nullptr); m_controller = controller; m_activeHangTimer.setTimeout(activeHangTime); } void CDStarRepeaterTRXThread::setControl(bool enabled, - const wxString& rpt1Callsign, const wxString& rpt2Callsign, - const wxString& shutdown,const wxString& startup, - const wxArrayString& command, const wxArrayString& status, - const wxArrayString& outputs) + const std::string& rpt1Callsign, const std::string& rpt2Callsign, + const std::string& shutdown, const std::string& startup, + const std::vector& command, const std::vector& commandLine, + const std::vector& status, const std::vector& outputs) { m_controlEnabled = enabled; @@ -463,39 +531,40 @@ void CDStarRepeaterTRXThread::setControl(bool enabled, m_controlShutdown = shutdown; m_controlStartup = startup; - m_controlStatus = status; - m_controlCommand = command; - m_controlOutput = outputs; + m_controlStatus = status; + m_controlCommand = command; + m_controlCommandLine = commandLine; + m_controlOutput = outputs; - for (size_t i = 0; i < m_controlCommand.GetCount(); ++i) { - m_controlCommand[i].Append(' ', LONG_CALLSIGN_LENGTH); - m_controlCommand[i].Truncate(LONG_CALLSIGN_LENGTH); + for (size_t i = 0; i < m_controlCommand.size(); ++i) { + m_controlCommand[i].append(LONG_CALLSIGN_LENGTH, ' '); + m_controlCommand[i].resize(LONG_CALLSIGN_LENGTH); } - for (size_t i = 0; i < m_controlStatus.GetCount(); ++i) { - m_controlStatus[i].Append(' ', LONG_CALLSIGN_LENGTH); - m_controlStatus[i].Truncate(LONG_CALLSIGN_LENGTH); + for (size_t i = 0; i < m_controlStatus.size(); ++i) { + m_controlStatus[i].append(LONG_CALLSIGN_LENGTH, ' '); + m_controlStatus[i].resize(LONG_CALLSIGN_LENGTH); } - for (size_t i = 0; i < m_controlOutput.GetCount(); ++i) { - m_controlOutput[i].Append(' ', LONG_CALLSIGN_LENGTH); - m_controlOutput[i].Truncate(LONG_CALLSIGN_LENGTH); + for (size_t i = 0; i < m_controlOutput.size(); ++i) { + m_controlOutput[i].append(LONG_CALLSIGN_LENGTH, ' '); + m_controlOutput[i].resize(LONG_CALLSIGN_LENGTH); } - m_controlRPT1.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_controlRPT2.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_controlShutdown.Append(wxT(' '), LONG_CALLSIGN_LENGTH); - m_controlStartup.Append(wxT(' '), LONG_CALLSIGN_LENGTH); + m_controlRPT1.append(LONG_CALLSIGN_LENGTH, ' '); + m_controlRPT2.append(LONG_CALLSIGN_LENGTH, ' '); + m_controlShutdown.append(LONG_CALLSIGN_LENGTH, ' '); + m_controlStartup.append(LONG_CALLSIGN_LENGTH, ' '); - m_controlRPT1.Truncate(LONG_CALLSIGN_LENGTH); - m_controlRPT2.Truncate(LONG_CALLSIGN_LENGTH); - m_controlShutdown.Truncate(LONG_CALLSIGN_LENGTH); - m_controlStartup.Truncate(LONG_CALLSIGN_LENGTH); + m_controlRPT1.resize(LONG_CALLSIGN_LENGTH); + m_controlRPT2.resize(LONG_CALLSIGN_LENGTH); + m_controlShutdown.resize(LONG_CALLSIGN_LENGTH); + m_controlStartup.resize(LONG_CALLSIGN_LENGTH); } void CDStarRepeaterTRXThread::setOutputs(bool out1, bool out2, bool out3, bool out4) { - if (m_controller == NULL) + if (m_controller == nullptr) return; m_output[0] = out1; @@ -509,42 +578,59 @@ void CDStarRepeaterTRXThread::setOutputs(bool out1, bool out2, bool out3, bool o m_controller->setOutput4(m_output[3]); } -void CDStarRepeaterTRXThread::setLogging(bool logging, const wxString& dir) +void CDStarRepeaterTRXThread::setLogging(bool logging, const std::string& dir) { - if (logging && m_logging == NULL) { + if (logging && m_logging == nullptr) { m_logging = new CDVTOOLFileWriter; m_logging->setDirectory(dir); return; } - if (!logging && m_logging != NULL) { + if (!logging && m_logging != nullptr) { delete m_logging; - m_logging = NULL; + m_logging = nullptr; return; } } void CDStarRepeaterTRXThread::setWhiteList(CCallsignList* list) { - wxASSERT(list != NULL); + assert(list != nullptr); m_whiteList = list; } void CDStarRepeaterTRXThread::setBlackList(CCallsignList* list) { - wxASSERT(list != NULL); + assert(list != nullptr); m_blackList = list; } void CDStarRepeaterTRXThread::setGreyList(CCallsignList* list) { - wxASSERT(list != NULL); + assert(list != nullptr); m_greyList = list; } +// --------------------------------------------------------------------------- +// receiveModem() — drain all pending events from the modem and dispatch them +// through the RX state machine. +// +// State: LISTENING +// HEADER event → validate and process the radio header. +// DATA event without a prior header → enter PROCESS_SLOW_DATA to attempt +// header recovery from the embedded slow-data stream. +// +// State: PROCESS_SLOW_DATA +// DATA events → feed the slow-data decoder looking for a late/embedded header. +// EOT / LOST → give up and return to LISTENING. +// +// State: PROCESS_DATA +// DATA events → process AMBE frames, relay to radio queue and network. +// EOT / LOST → synthesise a FRAME_END, call endOfRadioData(), return to LISTENING. +// --------------------------------------------------------------------------- void CDStarRepeaterTRXThread::receiveModem() { for (;;) { @@ -598,9 +684,9 @@ void CDStarRepeaterTRXThread::receiveModem() void CDStarRepeaterTRXThread::receiveHeader(CHeaderData* header) { - wxASSERT(header != NULL); + assert(header != nullptr); - wxLogMessage(wxT("Radio header decoded - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Radio header decoded - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); bool res = processRadioHeader(header); if (res) { @@ -609,7 +695,7 @@ void CDStarRepeaterTRXThread::receiveHeader(CHeaderData* header) setRadioState(DSRXS_PROCESS_DATA); } else { // This is a DD packet or some other problem - // wxLogMessage(wxT("Invalid header")); + // wxLogMessage("Invalid header"); } } @@ -622,11 +708,11 @@ void CDStarRepeaterTRXThread::receiveSlowData(unsigned char* data, unsigned int) // The data sync has been seen, a fuzzy match is used, two bit errors or less if (errs <= MAX_DATA_SYNC_BIT_ERRS) { - // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + // wxLogMessage("Found data sync at frame %u, errs: %u", m_radioSeqNo, errs); m_radioSeqNo = 0U; m_slowDataDecoder.sync(); } else if (m_radioSeqNo == 20U) { - // wxLogMessage(wxT("Assuming data sync")); + // wxLogMessage("Assuming data sync"); m_radioSeqNo = 0U; m_slowDataDecoder.sync(); } else { @@ -634,20 +720,18 @@ void CDStarRepeaterTRXThread::receiveSlowData(unsigned char* data, unsigned int) m_slowDataDecoder.addData(data + VOICE_FRAME_LENGTH_BYTES); CHeaderData* header = m_slowDataDecoder.getHeaderData(); - if (header == NULL) + if (header == nullptr) return; - wxLogMessage(wxT("Radio header from slow data - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X BER: 0%%"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Radio header from slow data - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X BER: 0%%", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); - if (header != NULL) { - bool res = processRadioHeader(header); - if (res) { - // A valid header and is a DV packet, go to normal data relaying - setRadioState(DSRXS_PROCESS_DATA); - } else { - // This is a DD packet or some other problem - // wxLogMessage(wxT("Invalid header")); - } + bool res = processRadioHeader(header); + if (res) { + // A valid header and is a DV packet, go to normal data relaying + setRadioState(DSRXS_PROCESS_DATA); + } else { + // This is a DD packet or some other problem + // wxLogMessage("Invalid header"); } } } @@ -661,11 +745,11 @@ void CDStarRepeaterTRXThread::receiveRadioData(unsigned char* data, unsigned int // The data sync has been seen, a fuzzy match is used, two bit errors or less if (errs <= MAX_DATA_SYNC_BIT_ERRS) { - // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + // wxLogMessage("Found data sync at frame %u, errs: %u", m_radioSeqNo, errs); m_radioSeqNo = 0U; processRadioFrame(data, FRAME_SYNC); } else if (m_radioSeqNo == 20U) { - // wxLogMessage(wxT("Regenerating data sync")); + // wxLogMessage("Regenerating data sync"); m_radioSeqNo = 0U; processRadioFrame(data, FRAME_SYNC); } else { @@ -674,9 +758,22 @@ void CDStarRepeaterTRXThread::receiveRadioData(unsigned char* data, unsigned int } } +// --------------------------------------------------------------------------- +// receiveNetwork() — drain all pending packets from the gateway and dispatch them. +// +// NETWORK_HEADER: validates RPT2 callsign, calls processNetworkHeader() which +// transitions to DSRS_NETWORK and enqueues the header for transmission. +// NETWORK_DATA: feeds processNetworkFrame() which handles sequence-number gap +// filling (silence or last-frame repeat) and queues frames for the modem. +// NETWORK_TEXT / TEMPTEXT: gateway-supplied slow-data text for the ack message. +// NETWORK_STATUS1..5: five user-status strings pushed by the gateway. +// +// After the read loop, checks whether any frames have arrived late (>200 ms gap) +// and inserts silence padding to keep the output stream continuous. +// --------------------------------------------------------------------------- void CDStarRepeaterTRXThread::receiveNetwork() { - if (m_protocolHandler == NULL) + if (m_protocolHandler == nullptr) return; NETWORK_TYPE type; @@ -689,13 +786,13 @@ void CDStarRepeaterTRXThread::receiveNetwork() break; } else if (type == NETWORK_HEADER) { // A header CHeaderData* header = m_protocolHandler->readHeader(); - if (header != NULL) { + if (header != nullptr) { ::memcpy(m_lastData, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); processNetworkHeader(header); - m_headerTime.Start(); - m_packetTime.Start(); + m_headerTime = steady_clock::now(); + m_packetTime = steady_clock::now(); m_packetCount = 0U; m_packetSilence = 0U; } @@ -713,38 +810,38 @@ void CDStarRepeaterTRXThread::receiveNetwork() } else if (type == NETWORK_TEXT) { // Slow data text for the Ack m_protocolHandler->readText(m_ackText, m_linkStatus, m_reflector); m_linkEncoder.setTextData(m_ackText); - wxLogMessage(wxT("Slow data set to \"%s\""), m_ackText.c_str()); + wxLogMessage("Slow data set to \"%s\"", m_ackText.c_str()); } else if (type == NETWORK_TEMPTEXT) { // Temporary slow data text for the Ack m_protocolHandler->readTempText(m_tempAckText); - wxLogMessage(wxT("Temporary slow data set to \"%s\""), m_tempAckText.c_str()); + wxLogMessage("Temporary slow data set to \"%s\"", m_tempAckText.c_str()); } else if (type == NETWORK_STATUS1) { // Status 1 data text m_statusText[0] = m_protocolHandler->readStatus1(); m_status1Encoder.setTextData(m_statusText[0]); - wxLogMessage(wxT("Status 1 data set to \"%s\""), m_statusText[0].c_str()); + wxLogMessage("Status 1 data set to \"%s\"", m_statusText[0].c_str()); } else if (type == NETWORK_STATUS2) { // Status 2 data text m_statusText[1] = m_protocolHandler->readStatus2(); m_status2Encoder.setTextData(m_statusText[1]); - wxLogMessage(wxT("Status 2 data set to \"%s\""), m_statusText[1].c_str()); + wxLogMessage("Status 2 data set to \"%s\"", m_statusText[1].c_str()); } else if (type == NETWORK_STATUS3) { // Status 3 data text m_statusText[2] = m_protocolHandler->readStatus3(); m_status3Encoder.setTextData(m_statusText[2]); - wxLogMessage(wxT("Status 3 data set to \"%s\""), m_statusText[2].c_str()); + wxLogMessage("Status 3 data set to \"%s\"", m_statusText[2].c_str()); } else if (type == NETWORK_STATUS4) { // Status 4 data text m_statusText[3] = m_protocolHandler->readStatus4(); m_status4Encoder.setTextData(m_statusText[3]); - wxLogMessage(wxT("Status 4 data set to \"%s\""), m_statusText[3].c_str()); + wxLogMessage("Status 4 data set to \"%s\"", m_statusText[3].c_str()); } else if (type == NETWORK_STATUS5) { // Status 5 data text m_statusText[4] = m_protocolHandler->readStatus5(); m_status5Encoder.setTextData(m_statusText[4]); - wxLogMessage(wxT("Status 5 data set to \"%s\""), m_statusText[4].c_str()); + wxLogMessage("Status 5 data set to \"%s\"", m_statusText[4].c_str()); } } // Have we missed any data frames? - if (m_rptState == DSRS_NETWORK && m_packetTime.Time() > 200L) { - unsigned int packetsNeeded = m_headerTime.Time() / DSTAR_FRAME_TIME_MS; - - // wxLogMessage(wxT("Time: %u ms, need %u packets and received %u packets"), ms - m_headerMS, packetsNeeded, m_packetCount); + long long packetMs = duration_cast(steady_clock::now() - m_packetTime).count(); + if (m_rptState == DSRS_NETWORK && packetMs > 200L) { + long long headerMs = duration_cast(steady_clock::now() - m_headerTime).count(); + unsigned int packetsNeeded = (unsigned int)(headerMs / DSTAR_FRAME_TIME_MS); if (packetsNeeded > m_packetCount) { unsigned int count = packetsNeeded - m_packetCount; @@ -752,8 +849,6 @@ void CDStarRepeaterTRXThread::receiveNetwork() if (count > 5U) { count -= 2U; - // wxLogMessage(wxT("Inserting %u silence frames into the network data stream"), count); - // Create silence frames for (unsigned int i = 0U; i < count; i++) { unsigned char data[DV_FRAME_LENGTH_BYTES]; @@ -764,13 +859,13 @@ void CDStarRepeaterTRXThread::receiveNetwork() } } - m_packetTime.Start(); + m_packetTime = steady_clock::now(); } } void CDStarRepeaterTRXThread::transmitLocalHeader(CHeaderData* header) { - wxLogMessage(wxT("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); m_headerEncoder.setHeaderData(*header); @@ -780,7 +875,7 @@ void CDStarRepeaterTRXThread::transmitLocalHeader(CHeaderData* header) void CDStarRepeaterTRXThread::transmitBeaconHeader() { - CHeaderData* header = new CHeaderData(m_rptCallsign, wxT("RPTR"), wxT("CQCQCQ "), m_gwyCallsign, m_rptCallsign); + CHeaderData* header = new CHeaderData(m_rptCallsign, "RPTR", "CQCQCQ ", m_gwyCallsign, m_rptCallsign); transmitLocalHeader(header); } @@ -804,7 +899,7 @@ void CDStarRepeaterTRXThread::transmitAnnouncementData(const unsigned char* data void CDStarRepeaterTRXThread::transmitRadioHeader(CHeaderData* header) { - wxLogMessage(wxT("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); m_headerEncoder.setHeaderData(*header); @@ -814,7 +909,7 @@ void CDStarRepeaterTRXThread::transmitRadioHeader(CHeaderData* header) void CDStarRepeaterTRXThread::transmitNetworkHeader(CHeaderData* header) { - wxLogMessage(wxT("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); m_headerEncoder.setHeaderData(*header); @@ -841,7 +936,7 @@ void CDStarRepeaterTRXThread::transmitNetworkHeader(CHeaderData* header) void CDStarRepeaterTRXThread::transmitStatus() { - CHeaderData* header = new CHeaderData(m_rptCallsign, wxT(" "), m_rxHeader->getMyCall1(), m_gwyCallsign, m_rptCallsign, RELAY_UNAVAILABLE); + CHeaderData* header = new CHeaderData(m_rptCallsign, " ", m_rxHeader->getMyCall1(), m_gwyCallsign, m_rptCallsign, RELAY_UNAVAILABLE); transmitLocalHeader(header); // Filler data @@ -869,7 +964,7 @@ void CDStarRepeaterTRXThread::transmitStatus() void CDStarRepeaterTRXThread::transmitErrorStatus() { - CHeaderData* header = new CHeaderData(m_rptCallsign, wxT(" "), m_rxHeader->getMyCall1(), m_rptCallsign, m_rptCallsign, RELAY_UNAVAILABLE); + CHeaderData* header = new CHeaderData(m_rptCallsign, " ", m_rxHeader->getMyCall1(), m_rptCallsign, m_rptCallsign, RELAY_UNAVAILABLE); transmitLocalHeader(header); // Filler data @@ -897,31 +992,31 @@ void CDStarRepeaterTRXThread::transmitErrorStatus() void CDStarRepeaterTRXThread::transmitUserStatus(unsigned int n) { - CSlowDataEncoder* encoder = NULL; - CHeaderData* header = NULL; + CSlowDataEncoder* encoder = nullptr; + CHeaderData* header = nullptr; switch (n) { case 0U: - header = new CHeaderData(m_rptCallsign, wxT(" "), wxT("STATUS 1"), m_gwyCallsign, m_rptCallsign); + header = new CHeaderData(m_rptCallsign, " ", "STATUS 1", m_gwyCallsign, m_rptCallsign); encoder = &m_status1Encoder; break; case 1U: - header = new CHeaderData(m_rptCallsign, wxT(" "), wxT("STATUS 2"), m_gwyCallsign, m_rptCallsign); + header = new CHeaderData(m_rptCallsign, " ", "STATUS 2", m_gwyCallsign, m_rptCallsign); encoder = &m_status2Encoder; break; case 2U: - header = new CHeaderData(m_rptCallsign, wxT(" "), wxT("STATUS 3"), m_gwyCallsign, m_rptCallsign); + header = new CHeaderData(m_rptCallsign, " ", "STATUS 3", m_gwyCallsign, m_rptCallsign); encoder = &m_status3Encoder; break; case 3U: - header = new CHeaderData(m_rptCallsign, wxT(" "), wxT("STATUS 4"), m_gwyCallsign, m_rptCallsign); + header = new CHeaderData(m_rptCallsign, " ", "STATUS 4", m_gwyCallsign, m_rptCallsign); encoder = &m_status4Encoder; break; case 4U: - header = new CHeaderData(m_rptCallsign, wxT(" "), wxT("STATUS 5"), m_gwyCallsign, m_rptCallsign); + header = new CHeaderData(m_rptCallsign, " ", "STATUS 5", m_gwyCallsign, m_rptCallsign); encoder = &m_status5Encoder; break; default: - wxLogWarning(wxT("Invalid status number - %u"), n); + wxLogWarning("Invalid status number - %u", n); return; } @@ -958,7 +1053,7 @@ void CDStarRepeaterTRXThread::transmitLocalHeader() return; CHeaderData* header = m_localQueue.getHeader(); - if (header == NULL) + if (header == nullptr) return; m_modem->writeHeader(*header); @@ -992,7 +1087,7 @@ void CDStarRepeaterTRXThread::transmitRadioHeader() return; CHeaderData* header = m_radioQueue.getHeader(); - if (header == NULL) + if (header == nullptr) return; m_modem->writeHeader(*header); @@ -1026,7 +1121,7 @@ void CDStarRepeaterTRXThread::transmitNetworkHeader() return; CHeaderData* header = m_networkQueue[m_readNum]->getHeader(); - if (header == NULL) + if (header == nullptr) return; m_modem->writeHeader(*header); @@ -1057,12 +1152,24 @@ void CDStarRepeaterTRXThread::transmitNetworkData() } } +// --------------------------------------------------------------------------- +// repeaterStateMachine() — timer-driven state transitions called once per loop. +// +// DSRS_VALID: timeout expiry → DSRS_TIMEOUT (user held PTT too long). +// DSRS_VALID_WAIT: ack timer expiry → transmit BER/status, return to LISTENING. +// DSRS_INVALID_WAIT:ack timer expiry → transmit error-status if configured, LISTENING. +// DSRS_TIMEOUT_WAIT:ack timer expiry → transmit status, return to LISTENING. +// DSRS_NETWORK: watchdog expiry → forcibly end the network transmission. +// +// The *_WAIT states exist so that the ack/status reply is deferred until the +// modem has fully stopped transmitting and PTT has dropped. +// --------------------------------------------------------------------------- void CDStarRepeaterTRXThread::repeaterStateMachine() { switch (m_rptState) { case DSRS_VALID: if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) { - wxLogMessage(wxT("User has timed out")); + wxLogMessage("User has timed out"); setRepeaterState(DSRS_TIMEOUT); } break; @@ -1096,7 +1203,7 @@ void CDStarRepeaterTRXThread::repeaterStateMachine() case DSRS_NETWORK: if (m_watchdogTimer.hasExpired()) { - wxLogMessage(wxT("Network watchdog has expired")); + wxLogMessage("Network watchdog has expired"); // Send end of transmission data to the radio m_networkQueue[m_writeNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); #if defined(MQTT) @@ -1114,7 +1221,7 @@ void CDStarRepeaterTRXThread::repeaterStateMachine() void CDStarRepeaterTRXThread::setRadioState(DSTAR_RX_STATE state) { - // This is the too state + // This is the to state switch (state) { case DSRXS_LISTENING: m_rxState = DSRXS_LISTENING; @@ -1138,6 +1245,20 @@ void CDStarRepeaterTRXThread::setRadioState(DSTAR_RX_STATE state) } } +// --------------------------------------------------------------------------- +// setRepeaterState() — transition the repeater state machine. +// +// Returns false without changing state if: +// - the repeater is disabled/shutdown, or +// - the requested transition is not allowed from the current state +// (e.g. DSRS_VALID is only reachable from LISTENING or VALID_WAIT). +// +// On exit from LISTENING the beacon timer is stopped (no beacons mid-QSO). +// On entry to LISTENING the beacon timer restarts and the gateway is reset. +// On entry to DSRS_VALID the timeout timer starts (or the ack timer stops +// if we are re-keying from VALID_WAIT). +// On entry to DSRS_NETWORK the watchdog starts to guard against gateway silence. +// --------------------------------------------------------------------------- bool CDStarRepeaterTRXThread::setRepeaterState(DSTAR_RPT_STATE state) { // Can't change state when shutdown @@ -1179,7 +1300,7 @@ bool CDStarRepeaterTRXThread::setRepeaterState(DSTAR_RPT_STATE state) m_ackTimer.stop(); m_beaconTimer.start(); m_rptState = DSRS_LISTENING; - if (m_protocolHandler != NULL) // Tell the protocol handler + if (m_protocolHandler != nullptr) // Tell the protocol handler m_protocolHandler->reset(); break; @@ -1206,7 +1327,7 @@ bool CDStarRepeaterTRXThread::setRepeaterState(DSTAR_RPT_STATE state) if (m_mode == MODE_DUPLEX) m_radioQueue.addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); - if (!m_blocked && m_protocolHandler != NULL) { + if (!m_blocked && m_protocolHandler != nullptr) { unsigned char bytes[DV_FRAME_MAX_LENGTH_BYTES]; ::memcpy(bytes, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); ::memcpy(bytes + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); @@ -1254,9 +1375,28 @@ bool CDStarRepeaterTRXThread::setRepeaterState(DSTAR_RPT_STATE state) return true; } +// --------------------------------------------------------------------------- +// processRadioHeader() — validate and dispatch an incoming radio header. +// +// Processing order: +// 1. checkControl() — DTMF control commands (shutdown, startup, status, etc.) +// 2. checkAnnouncements() — announcement record/delete triggers. +// 3. Shutdown guard — drop silently when in DSRS_SHUTDOWN. +// 4. White-list check — reject if callsign not in the white list. +// 5. Black-list check — reject if callsign is in the black list. +// 6. Grey-list check — allow locally, but suppress network forwarding. +// 7. Gateway-mode flag-2 — detect and drop our own retransmitted headers. +// 8. DD packet check — reject D-Data (non-voice) frames. +// 9. checkHeader() — structural and callsign format validation. +// 10. DSRS_NETWORK guard — repeater busy: send a busy header to the network if appropriate. +// 11. setRepeaterState(DSRS_VALID) — accept the transmission; forward header to network and RF. +// +// Returns true if the header was consumed (even if ultimately rejected), false +// only for DD packets (the caller uses this to stay in LISTENING). +// --------------------------------------------------------------------------- bool CDStarRepeaterTRXThread::processRadioHeader(CHeaderData* header) { - wxASSERT(header != NULL); + assert(header != nullptr); // Check control messages bool res = checkControl(*header); @@ -1284,29 +1424,29 @@ bool CDStarRepeaterTRXThread::processRadioHeader(CHeaderData* header) return true; } - if (m_whiteList != NULL) { + if (m_whiteList != nullptr) { bool res = m_whiteList->isInList(header->getMyCall1()); if (!res) { - wxLogMessage(wxT("%s rejected due to not being in the white list"), header->getMyCall1().c_str()); + wxLogMessage("%s rejected due to not being in the white list", header->getMyCall1().c_str()); delete header; return true; } } - if (m_blackList != NULL) { + if (m_blackList != nullptr) { bool res = m_blackList->isInList(header->getMyCall1()); if (res) { - wxLogMessage(wxT("%s rejected due to being in the black list"), header->getMyCall1().c_str()); + wxLogMessage("%s rejected due to being in the black list", header->getMyCall1().c_str()); delete header; return true; } } m_blocked = false; - if (m_greyList != NULL) { + if (m_greyList != nullptr) { bool res = m_greyList->isInList(header->getMyCall1()); if (res) { - wxLogMessage(wxT("%s blocked from the network due to being in the grey list"), header->getMyCall1().c_str()); + wxLogMessage("%s blocked from the network due to being in the grey list", header->getMyCall1().c_str()); m_blocked = true; } } @@ -1314,7 +1454,7 @@ bool CDStarRepeaterTRXThread::processRadioHeader(CHeaderData* header) // Check for receiving our own gateway data, and ignore it if (m_mode == MODE_GATEWAY) { if (header->getFlag2() == 0x01U) { - wxLogMessage(wxT("Receiving a gateway header, ignoring")); + wxLogMessage("Receiving a gateway header, ignoring"); delete header; return true; } @@ -1324,7 +1464,7 @@ bool CDStarRepeaterTRXThread::processRadioHeader(CHeaderData* header) // We don't handle DD data packets if (header->isDataPacket()) { - wxLogMessage(wxT("Received a DD packet, ignoring")); + wxLogMessage("Received a DD packet, ignoring"); delete header; return false; } @@ -1350,12 +1490,11 @@ bool CDStarRepeaterTRXThread::processRadioHeader(CHeaderData* header) break; } - // If we're in network mode, send the header as a busy header to the gateway in case it's an unlink - // command + // If we're in network mode, send the header as a busy header to the gateway in case it's an unlink command if (m_rptState == DSRS_NETWORK) { // Only send on the network if the user isn't blocked we have one and RPT2 is not blank or the repeater callsign - if (!header->getRptCall2().IsSameAs(wxT(" ")) && !header->getRptCall2().IsSameAs(m_rptCallsign)) { - if (!m_blocked && m_protocolHandler != NULL) { + if (header->getRptCall2() != " " && header->getRptCall2() != m_rptCallsign) { + if (!m_blocked && m_protocolHandler != nullptr) { CHeaderData netHeader(*header); netHeader.setRptCall1(header->getRptCall2()); netHeader.setRptCall2(header->getRptCall1()); @@ -1382,11 +1521,11 @@ bool CDStarRepeaterTRXThread::processRadioHeader(CHeaderData* header) m_rxHeader->getYourCall(), m_rxHeader->getRptCall2(), "rf"); #endif - if (m_logging != NULL) + if (m_logging != nullptr) m_logging->open(*m_rxHeader); // Only send on the network if the user isn't blocked and we have one and RPT2 is not blank or the repeater callsign - if (!m_blocked && m_protocolHandler != NULL && !m_rxHeader->getRptCall2().IsSameAs(wxT(" ")) && !m_rxHeader->getRptCall2().IsSameAs(m_rptCallsign)) { + if (!m_blocked && m_protocolHandler != nullptr && m_rxHeader->getRptCall2() != " " && m_rxHeader->getRptCall2() != m_rptCallsign) { CHeaderData netHeader(*m_rxHeader); netHeader.setRptCall1(m_rxHeader->getRptCall2()); netHeader.setRptCall2(m_rxHeader->getRptCall1()); @@ -1407,9 +1546,17 @@ bool CDStarRepeaterTRXThread::processRadioHeader(CHeaderData* header) return true; } +// --------------------------------------------------------------------------- +// processNetworkHeader() — handle a header arriving from the gateway. +// +// Validates that RPT2 matches our callsign, then transitions to DSRS_NETWORK. +// In GATEWAY mode the header is modified before transmission: the repeater bit +// is set, flag2 is set to 0x01 (marks gateway origin), and RPT1/RPT2 are +// swapped to the local pair. In other modes the header is forwarded unchanged. +// --------------------------------------------------------------------------- void CDStarRepeaterTRXThread::processNetworkHeader(CHeaderData* header) { - wxASSERT(header != NULL); + assert(header != nullptr); // If shutdown we ignore incoming headers if (m_rptState == DSRS_SHUTDOWN) { @@ -1417,11 +1564,11 @@ void CDStarRepeaterTRXThread::processNetworkHeader(CHeaderData* header) return; } - wxLogMessage(wxT("Network header received - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Network header received - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); // Is it for us? - if (!header->getRptCall2().IsSameAs(m_rptCallsign)) { - wxLogMessage(wxT("Invalid network RPT2 value, ignoring")); + if (header->getRptCall2() != m_rptCallsign) { + wxLogMessage("Invalid network RPT2 value, ignoring"); delete header; return; } @@ -1499,7 +1646,7 @@ void CDStarRepeaterTRXThread::processRadioFrame(unsigned char* data, FRAME_TYPE // Pass background AMBE data to the network if (m_busyData) { if (type == FRAME_END) { - if (!m_blocked && m_protocolHandler != NULL) { + if (!m_blocked && m_protocolHandler != nullptr) { unsigned char bytes[DV_FRAME_MAX_LENGTH_BYTES]; ::memcpy(bytes, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); ::memcpy(bytes + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); @@ -1508,7 +1655,7 @@ void CDStarRepeaterTRXThread::processRadioFrame(unsigned char* data, FRAME_TYPE m_busyData = false; } else { - if (!m_blocked && m_protocolHandler != NULL) + if (!m_blocked && m_protocolHandler != nullptr) m_protocolHandler->writeBusyData(data, DV_FRAME_LENGTH_BYTES, errors, false); } @@ -1520,7 +1667,7 @@ void CDStarRepeaterTRXThread::processRadioFrame(unsigned char* data, FRAME_TYPE return; if (type == FRAME_END) { - if (m_logging != NULL) + if (m_logging != nullptr) m_logging->close(); // Transmit the end sync on the radio, but only in duplex mode @@ -1528,18 +1675,18 @@ void CDStarRepeaterTRXThread::processRadioFrame(unsigned char* data, FRAME_TYPE m_radioQueue.addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); // Send null data and the end marker over the network, and the statistics - if (!m_blocked && m_protocolHandler != NULL) { + if (!m_blocked && m_protocolHandler != nullptr) { unsigned char bytes[DV_FRAME_MAX_LENGTH_BYTES]; ::memcpy(bytes, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); ::memcpy(bytes + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); m_protocolHandler->writeData(bytes, DV_FRAME_MAX_LENGTH_BYTES, 0U, true); } } else { - if (m_logging != NULL) + if (m_logging != nullptr) m_logging->write(data, DV_FRAME_LENGTH_BYTES); // Send the data to the network - if (!m_blocked && m_protocolHandler != NULL) + if (!m_blocked && m_protocolHandler != nullptr) m_protocolHandler->writeData(data, DV_FRAME_LENGTH_BYTES, errors, false); // Send the data for transmission, but only in duplex mode @@ -1553,8 +1700,8 @@ void CDStarRepeaterTRXThread::processRadioFrame(unsigned char* data, FRAME_TYPE unsigned int CDStarRepeaterTRXThread::processNetworkFrame(unsigned char* data, unsigned int length, unsigned char seqNo) { - wxASSERT(data != NULL); - wxASSERT(length > 0U); + assert(data != nullptr); + assert(length > 0U); if (m_rptState != DSRS_NETWORK) return 0U; @@ -1654,27 +1801,37 @@ unsigned int CDStarRepeaterTRXThread::processNetworkFrame(unsigned char* data, u return packetCount; } +// --------------------------------------------------------------------------- +// endOfRadioData() — called when the radio transmission ends (EOT or LOST). +// +// Logs the AMBE statistics (duration, silence %, BER). Depending on state: +// DSRS_VALID: formats the ack text (BER or custom text), transitions to +// DSRS_VALID_WAIT (which later sends the ack and returns to LISTENING). +// DSRS_INVALID: transitions to DSRS_INVALID_WAIT for a possible error reply. +// DSRS_TIMEOUT: same ack logic as VALID, then DSRS_TIMEOUT_WAIT. +// If AT_NONE is configured, skips the wait states and goes straight to LISTENING. +// --------------------------------------------------------------------------- void CDStarRepeaterTRXThread::endOfRadioData() { switch (m_rptState) { case DSRS_VALID: - wxLogMessage(wxT("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%"), m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); + wxLogMessage("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%", m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); - if (m_tempAckText.IsEmpty()) { + if (m_tempAckText.empty()) { if (m_ack == AT_BER) { // Create the ack text with the linked reflector and BER - wxString ackText; + char ackBuf[64]; if (m_linkStatus == LS_LINKED_DEXTRA || m_linkStatus == LS_LINKED_DPLUS || m_linkStatus == LS_LINKED_DCS || m_linkStatus == LS_LINKED_CCS || m_linkStatus == LS_LINKED_LOOPBACK) - ackText.Printf(wxT("%-8s BER: %.1f%% "), m_reflector.c_str(), float(m_ambeErrors * 100U) / float(m_ambeBits)); + ::snprintf(ackBuf, sizeof(ackBuf), "%-8s BER: %.1f%% ", m_reflector.c_str(), float(m_ambeErrors * 100U) / float(m_ambeBits)); else - ackText.Printf(wxT("BER: %.1f%% "), float(m_ambeErrors * 100U) / float(m_ambeBits)); - m_ackEncoder.setTextData(ackText); + ::snprintf(ackBuf, sizeof(ackBuf), "BER: %.1f%% ", float(m_ambeErrors * 100U) / float(m_ambeBits)); + m_ackEncoder.setTextData(std::string(ackBuf)); } else { m_ackEncoder.setTextData(m_ackText); } } else { m_ackEncoder.setTextData(m_tempAckText); - m_tempAckText.Clear(); + m_tempAckText.clear(); } if (m_ack != AT_NONE || m_mode == MODE_GATEWAY) { @@ -1686,7 +1843,7 @@ void CDStarRepeaterTRXThread::endOfRadioData() break; case DSRS_INVALID: - wxLogMessage(wxT("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%"), m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); + wxLogMessage("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%", m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); if (m_ack != AT_NONE || m_mode == MODE_GATEWAY) { setRepeaterState(DSRS_INVALID_WAIT); @@ -1697,23 +1854,23 @@ void CDStarRepeaterTRXThread::endOfRadioData() break; case DSRS_TIMEOUT: - wxLogMessage(wxT("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%"), m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); + wxLogMessage("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%", m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); - if (m_tempAckText.IsEmpty()) { + if (m_tempAckText.empty()) { if (m_ack == AT_BER) { // Create the ack text with the linked reflector and BER - wxString ackText; + char ackBuf[64]; if (m_linkStatus == LS_LINKED_DEXTRA || m_linkStatus == LS_LINKED_DPLUS || m_linkStatus == LS_LINKED_DCS || m_linkStatus == LS_LINKED_CCS || m_linkStatus == LS_LINKED_LOOPBACK) - ackText.Printf(wxT("%-8s BER: %.1f%% "), m_reflector.c_str(), float(m_ambeErrors * 100U) / float(m_ambeBits)); + ::snprintf(ackBuf, sizeof(ackBuf), "%-8s BER: %.1f%% ", m_reflector.c_str(), float(m_ambeErrors * 100U) / float(m_ambeBits)); else - ackText.Printf(wxT("BER: %.1f%% "), float(m_ambeErrors * 100U) / float(m_ambeBits)); - m_ackEncoder.setTextData(ackText); + ::snprintf(ackBuf, sizeof(ackBuf), "BER: %.1f%% ", float(m_ambeErrors * 100U) / float(m_ambeBits)); + m_ackEncoder.setTextData(std::string(ackBuf)); } else { m_ackEncoder.setTextData(m_ackText); } } else { m_ackEncoder.setTextData(m_tempAckText); - m_tempAckText.Clear(); + m_tempAckText.clear(); } if (m_ack != AT_NONE || m_mode == MODE_GATEWAY) { @@ -1729,13 +1886,24 @@ void CDStarRepeaterTRXThread::endOfRadioData() } } +// --------------------------------------------------------------------------- +// endOfNetworkData() — called when a network transmission ends (end-of-frame +// flag received, or watchdog expiry). +// +// Logs packet-loss statistics, returns to DSRS_LISTENING, starts the active- +// hang timer, resets the gateway protocol handler, and advances the write-side +// of the double-buffered network queue to the next slot. +// --------------------------------------------------------------------------- void CDStarRepeaterTRXThread::endOfNetworkData() { float loss = 0.0F; if (m_packetCount != 0U) loss = float(m_packetSilence) / float(m_packetCount); - wxLogMessage(wxT("Stats for %s Frames: %.1fs, Loss: %.1f%%, Packets: %u/%u"), m_rxHeader->getMyCall1().c_str(), float(m_packetCount) / 50.0F, loss * 100.0F, m_packetSilence, m_packetCount); + if (m_rxHeader != nullptr) + wxLogMessage("Stats for %s Frames: %.1fs, Loss: %.1f%%, Packets: %u/%u", m_rxHeader->getMyCall1().c_str(), float(m_packetCount) / 50.0F, loss * 100.0F, m_packetSilence, m_packetCount); + else + wxLogMessage("Stats for Network Frames: %.1fs, Loss: %.1f%%, Packets: %u/%u", float(m_packetCount) / 50.0F, loss * 100.0F, m_packetSilence, m_packetCount); setRepeaterState(DSRS_LISTENING); m_activeHangTimer.start(); @@ -1749,11 +1917,11 @@ CDStarRepeaterStatusData* CDStarRepeaterTRXThread::getStatus() { CDStarRepeaterStatusData* status; if (m_rptState == DSRS_SHUTDOWN || m_rptState == DSRS_LISTENING) { - status = new CDStarRepeaterStatusData(wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, - wxEmptyString, 0x00, 0x00, 0x00, m_tx, m_rxState, m_rptState, m_timeoutTimer.getTimer(), - m_timeoutTimer.getTimeout(), m_beaconTimer.getTimer(), m_beaconTimer.getTimeout(), 0.0F, - m_announcementTimer.getTimer(), m_announcementTimer.getTimeout(), m_ackText, m_statusText[0], - m_statusText[1], m_statusText[2], m_statusText[3], m_statusText[4]); + status = new CDStarRepeaterStatusData(std::string(), std::string(), std::string(), std::string(), + std::string(), 0x00, 0x00, 0x00, m_tx, m_rxState, m_rptState, m_timeoutTimer.getTimer(), + m_timeoutTimer.getTimeout(), m_beaconTimer.getTimer(), m_beaconTimer.getTimeout(), + m_announcementTimer.getTimer(), m_announcementTimer.getTimeout(), 0.0F, + m_ackText, m_statusText[0], m_statusText[1], m_statusText[2], m_statusText[3], m_statusText[4]); } else if (m_rptState == DSRS_NETWORK) { float loss = 0.0F; if (m_packetCount != 0U) @@ -1783,7 +1951,7 @@ CDStarRepeaterStatusData* CDStarRepeaterTRXThread::getStatus() m_statusText[4]); } - if (m_type.IsSameAs(wxT("DVAP")) && m_modem != NULL) { + if (m_type == "DVAP" && m_modem != nullptr) { CDVAPController* dvap = static_cast(m_modem); bool squelch = dvap->getSquelch(); int signal = dvap->getSignal(); @@ -1811,9 +1979,9 @@ void CDStarRepeaterTRXThread::clock(unsigned int ms) #if defined(MQTT) m_mqttStatusTimer.clock(ms); #endif - if (m_beacon != NULL) + if (m_beacon != nullptr) m_beacon->clock(); - if (m_announcement != NULL) + if (m_announcement != nullptr) m_announcement->clock(); } @@ -1827,37 +1995,55 @@ void CDStarRepeaterTRXThread::startup() m_shutdown = false; } +// --------------------------------------------------------------------------- +// checkControl() — test the header's YOUR call against configured control +// destinations. Returns true (and consumes the transmission) if any match. +// +// Matches are checked in priority order: +// 1. Command slots (up to 6): execute the configured shell command via system(). +// 2. Status slots (up to 5): start a 3 s timer; on expiry transmit a status announcement. +// 3. Output slots (up to 4): toggle a controller output line. +// 4. Shutdown / Startup: change the m_shutdown flag. +// The RPT1/RPT2 pair must also match m_controlRPT1/RPT2 for any check to proceed. +// --------------------------------------------------------------------------- bool CDStarRepeaterTRXThread::checkControl(const CHeaderData& header) { if (!m_controlEnabled) return false; - if (!m_controlRPT1.IsSameAs(header.getRptCall1()) || !m_controlRPT2.IsSameAs(header.getRptCall2())) + if (m_controlRPT1 != header.getRptCall1() || m_controlRPT2 != header.getRptCall2()) return false; - for (size_t i = 0; i < m_controlCommand.GetCount(); ++i) { - if (m_controlCommand[i].IsSameAs(header.getYourCall())) { - wxThreadEvent evt(wxEVT_THREAD, wxEVT_THREAD_COMMAND); - evt.SetInt(i); - wxTheApp->QueueEvent(evt.Clone()); - wxLogMessage("Command %d requested by %s/%s", i, header.getMyCall1().c_str(), header.getMyCall2().c_str()); + for (size_t i = 0; i < m_controlCommand.size(); ++i) { + if (m_controlCommand[i] == header.getYourCall()) { + if (i < m_controlCommandLine.size() && !m_controlCommandLine[i].empty()) { + wxLogMessage("Executing command %u (%s) requested by %s/%s", + (unsigned)i + 1U, m_controlCommandLine[i].c_str(), + header.getMyCall1().c_str(), header.getMyCall2().c_str()); + int ret = ::system(m_controlCommandLine[i].c_str()); + if (ret != 0) + wxLogWarning("Command %u exited with status %d", (unsigned)i + 1U, ret); + } else { + wxLogMessage("Command %u requested by %s/%s (no command line configured)", + (unsigned)i + 1U, header.getMyCall1().c_str(), header.getMyCall2().c_str()); + } return true; } } - for (size_t i = 0; i < m_controlStatus.GetCount(); ++i) { - if (m_controlStatus[i].IsSameAs(header.getYourCall())) { - wxLogMessage(wxT("Status %d requested by %s/%s"), - i, header.getMyCall1().c_str(), + for (size_t i = 0; i < m_controlStatus.size(); ++i) { + if (m_controlStatus[i] == header.getYourCall()) { + wxLogMessage("Status %d requested by %s/%s", + (int)i, header.getMyCall1().c_str(), header.getMyCall2().c_str()); m_statusAnnounceTimer[i].start(); return true; } } - for (size_t i = 0; i < m_controlOutput.GetCount(); ++i) { - if (m_controlOutput[i].IsSameAs(header.getYourCall())) { - wxLogMessage(wxT("Output %d requested by %s/%s"), i, + for (size_t i = 0; i < m_controlOutput.size(); ++i) { + if (m_controlOutput[i] == header.getYourCall()) { + wxLogMessage("Output %d requested by %s/%s", (int)i, header.getMyCall1().c_str(), header.getMyCall2().c_str()); m_output[i] = !m_output[i]; @@ -1871,33 +2057,43 @@ bool CDStarRepeaterTRXThread::checkControl(const CHeaderData& header) } } - if (m_controlShutdown.IsSameAs(header.getYourCall())) { - wxLogMessage(wxT("Shutdown requested by %s/%s"), header.getMyCall1().c_str(), header.getMyCall2().c_str()); + if (m_controlShutdown == header.getYourCall()) { + wxLogMessage("Shutdown requested by %s/%s", header.getMyCall1().c_str(), header.getMyCall2().c_str()); shutdown(); - } else if (m_controlStartup.IsSameAs(header.getYourCall())) { - wxLogMessage(wxT("Startup requested by %s/%s"), header.getMyCall1().c_str(), header.getMyCall2().c_str()); + } else if (m_controlStartup == header.getYourCall()) { + wxLogMessage("Startup requested by %s/%s", header.getMyCall1().c_str(), header.getMyCall2().c_str()); startup(); } else { - wxLogMessage(wxT("Invalid command of %s sent by %s/%s"), header.getYourCall().c_str(), header.getMyCall1().c_str(), header.getMyCall2().c_str()); + wxLogMessage("Invalid command of %s sent by %s/%s", header.getYourCall().c_str(), header.getMyCall1().c_str(), header.getMyCall2().c_str()); } return true; } +// --------------------------------------------------------------------------- +// checkAnnouncements() — test the header's RPT1/RPT2 pair against the +// configured announcement record and delete callsign pairs. +// +// Record match: sets m_recording = true; subsequent audio frames are written +// to the CAnnouncementUnit rather than forwarded. +// Delete match: sets m_deleting = true; audio frames are silently discarded +// and the existing announcement is deleted. +// Returns true if either match fires (the transmission is consumed by this path). +// --------------------------------------------------------------------------- bool CDStarRepeaterTRXThread::checkAnnouncements(const CHeaderData& header) { - if (m_announcement == NULL) + if (m_announcement == nullptr) return false; - if (m_recordRPT1.IsSameAs(header.getRptCall1()) && m_recordRPT2.IsSameAs(header.getRptCall2())) { - wxLogMessage(wxT("Announcement creation requested by %s/%s"), header.getMyCall1().c_str(), header.getMyCall2().c_str()); + if (m_recordRPT1 == header.getRptCall1() && m_recordRPT2 == header.getRptCall2()) { + wxLogMessage("Announcement creation requested by %s/%s", header.getMyCall1().c_str(), header.getMyCall2().c_str()); m_announcement->writeHeader(header); m_recording = true; return true; } - if (m_deleteRPT1.IsSameAs(header.getRptCall1()) && m_deleteRPT2.IsSameAs(header.getRptCall2())) { - wxLogMessage(wxT("Announcement deletion requested by %s/%s"), header.getMyCall1().c_str(), header.getMyCall2().c_str()); + if (m_deleteRPT1 == header.getRptCall1() && m_deleteRPT2 == header.getRptCall2()) { + wxLogMessage("Announcement deletion requested by %s/%s", header.getMyCall1().c_str(), header.getMyCall2().c_str()); m_announcement->deleteAnnouncement(); m_deleting = true; return true; @@ -1906,6 +2102,25 @@ bool CDStarRepeaterTRXThread::checkAnnouncements(const CHeaderData& header) return false; } +// --------------------------------------------------------------------------- +// checkHeader() — structural and callsign-format validation. Returns: +// STATE_TRUE — header is acceptable; proceed with the transmission. +// STATE_FALSE — header is structurally invalid (wrong RPT1, non-repeater +// mode, etc.); transition to DSRS_INVALID and send error reply. +// STATE_UNKNOWN — header looks ill-formed but silently drop (ack suppressed). +// +// Checks performed: +// - RPT1 validation / simplex→repeater conversion (m_rpt1Validation flag). +// - Repeater-mode bit must be set outside of GATEWAY mode. +// - Gateway mode: reject acks and our own callsigns in YOUR. +// - MYCALL must not be empty, "NOCALL", "N0CALL", or the repeater callsign. +// - STN* prefix is exempted from MYCALL checks (used by some terminal modes). +// - French class-3 novice (F0xxx) and Australian foundation (VKnFxxx) callsigns +// are rejected because they are not licensed for digital repeater operation. +// - MYCALL must match the standard amateur callsign regex. +// - RPT1 must match our own callsign. +// - Callsign restriction: MYCALL prefix must match the repeater prefix. +// --------------------------------------------------------------------------- TRISTATE CDStarRepeaterTRXThread::checkHeader(CHeaderData& header) { // If not in RPT1 validation mode, then a simplex header is converted to a proper repeater header @@ -1921,7 +2136,7 @@ TRISTATE CDStarRepeaterTRXThread::checkHeader(CHeaderData& header) // The repeater bit must be set when not in gateway mode if (m_mode != MODE_GATEWAY) { if (!header.isRepeaterMode()) { - wxLogMessage(wxT("Received a non-repeater packet, ignoring")); + wxLogMessage("Received a non-repeater packet, ignoring"); return STATE_FALSE; } } else { @@ -1930,60 +2145,60 @@ TRISTATE CDStarRepeaterTRXThread::checkHeader(CHeaderData& header) return STATE_UNKNOWN; // As a second check, reject on own UR call - wxString ur = header.getYourCall(); - if (ur.IsSameAs(m_rptCallsign) || ur.IsSameAs(m_gwyCallsign)) + std::string ur = header.getYourCall(); + if (ur == m_rptCallsign || ur == m_gwyCallsign) return STATE_UNKNOWN; // Change RPT2 to be the gateway callsign in gateway mode header.setRptCall2(m_gwyCallsign); } - wxString my = header.getMyCall1(); + std::string my = header.getMyCall1(); // Make sure MyCall is not empty, a silly value, or the repeater or gateway callsigns, STN* is a special case - if (!my.Left(3U).IsSameAs(wxT("STN"))) { - if (my.IsSameAs(m_rptCallsign) || - my.IsSameAs(m_gwyCallsign) || - my.IsSameAs(wxT(" ")) || - my.Left(6U).IsSameAs(wxT("NOCALL")) || - my.Left(6U).IsSameAs(wxT("N0CALL")) || - my.Left(6U).IsSameAs(wxT("MYCALL"))) { - wxLogMessage(wxT("Invalid MYCALL value of %s, ignoring"), my.c_str()); + if (my.compare(0, 3, "STN") != 0) { + if (my == m_rptCallsign || + my == m_gwyCallsign || + my == " " || + my.compare(0, 6, "NOCALL") == 0 || + my.compare(0, 6, "N0CALL") == 0 || + my.compare(0, 6, "MYCALL") == 0) { + wxLogMessage("Invalid MYCALL value of %s, ignoring", my.c_str()); return STATE_UNKNOWN; } } // Check for a French class 3 novice callsign, and reject // Of the form F0xxx - if (my.Left(2U).IsSameAs(wxT("F0"))) { - wxLogMessage(wxT("French novice class licence callsign found, %s, ignoring"), my.c_str()); + if (my.compare(0, 2, "F0") == 0) { + wxLogMessage("French novice class licence callsign found, %s, ignoring", my.c_str()); return STATE_UNKNOWN; } // Check for an Australian foundation class licence callsign, and reject // Of the form VKnFxxx - if (my.Left(2U).IsSameAs(wxT("VK")) && my.GetChar(3U) == wxT('F') && my.GetChar(6U) != wxT(' ')) { - wxLogMessage(wxT("Australian foundation class licence callsign found, %s, ignoring"), my.c_str()); + if (my.compare(0, 2, "VK") == 0 && my.length() > 6 && my[3] == 'F' && my[6] != ' ') { + wxLogMessage("Australian foundation class licence callsign found, %s, ignoring", my.c_str()); return STATE_UNKNOWN; } // Check the MyCall value against the regular expression - bool ok = m_regEx.Matches(my); + bool ok = std::regex_search(my, m_regEx); if (!ok) { - wxLogMessage(wxT("Invalid MYCALL value of %s, ignoring"), my.c_str()); + wxLogMessage("Invalid MYCALL value of %s, ignoring", my.c_str()); return STATE_UNKNOWN; } // Is it for us? - if (!header.getRptCall1().IsSameAs(m_rptCallsign)) { - wxLogMessage(wxT("Invalid RPT1 value %s, ignoring"), header.getRptCall1().c_str()); + if (header.getRptCall1() != m_rptCallsign) { + wxLogMessage("Invalid RPT1 value %s, ignoring", header.getRptCall1().c_str()); return STATE_FALSE; } // If using callsign restriction, validate the my callsign if (m_restriction) { - if (!my.Left(LONG_CALLSIGN_LENGTH - 1U).IsSameAs(m_rptCallsign.Left(LONG_CALLSIGN_LENGTH - 1U))) { - wxLogMessage(wxT("Unauthorised user %s tried to access the repeater"), my.c_str()); + if (my.substr(0, LONG_CALLSIGN_LENGTH - 1U) != m_rptCallsign.substr(0, LONG_CALLSIGN_LENGTH - 1U)) { + wxLogMessage("Unauthorised user %s tried to access the repeater", my.c_str()); return STATE_UNKNOWN; } } @@ -2015,9 +2230,19 @@ unsigned int CDStarRepeaterTRXThread::countBits(unsigned char byte) return bits; } +// --------------------------------------------------------------------------- +// blankDTMF() — replace the voice portion of an AMBE frame with silence if +// the frame contains a DTMF tone signature. +// +// D-Star embeds DTMF detection info in specific bit positions of the AMBE +// frame. Applying DTMF_MASK and comparing against DTMF_SIG identifies tones. +// When detected, the 9-byte voice portion is replaced with NULL_AMBE_DATA_BYTES +// (the AMBE-encoded silence pattern), preventing DTMF from being heard on air +// or forwarded to the network. The 3-byte slow-data field is left intact. +// --------------------------------------------------------------------------- void CDStarRepeaterTRXThread::blankDTMF(unsigned char* data) { - wxASSERT(data != NULL); + assert(data != nullptr); // DTMF begins with these byte values if ((data[0] & DTMF_MASK[0]) == DTMF_SIG[0] && (data[1] & DTMF_MASK[1]) == DTMF_SIG[1] && diff --git a/DStarRepeater/DStarRepeaterTRXThread.h b/DStarRepeater/DStarRepeaterTRXThread.h index feccad9..fb29b6c 100644 --- a/DStarRepeater/DStarRepeaterTRXThread.h +++ b/DStarRepeater/DStarRepeaterTRXThread.h @@ -40,30 +40,41 @@ #include "MQTTPublisher.h" #endif -#include -#include - +#include "StdCompat.h" +#include +#include +#include + +// CDStarRepeaterTRXThread — the primary repeater thread used for DUPLEX, +// SIMPLEX, and GATEWAY modes. It owns both the receive path (radio → network) +// and the transmit path (network → radio) and is the only thread variant that +// implements the full repeater state machine, DTMF blanking, ack/status +// transmissions, beacons, announcements, and DTMF-triggered control commands. +// +// Implements IBeaconCallback and IAnnouncementCallback so that CBeaconUnit and +// CAnnouncementUnit can push pre-built audio frames back into the local queue. class CDStarRepeaterTRXThread : public IDStarRepeaterThread, public IBeaconCallback, public IAnnouncementCallback { public: - CDStarRepeaterTRXThread(const wxString& type); + CDStarRepeaterTRXThread(const std::string& type); virtual ~CDStarRepeaterTRXThread(); - virtual void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); + virtual void setCallsign(const std::string& callsign, const std::string& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); virtual void setProtocolHandler(CRepeaterProtocolHandler* handler, bool local); virtual void setModem(CModem* modem); virtual void setController(CExternalController* controller, unsigned int activeHangTime); virtual void setTimes(unsigned int timeout, unsigned int ackTime); - virtual void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language); - virtual void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); - - virtual void setControl(bool enabled, const wxString& rpt1Callsign, - const wxString& rpt2Callsign, const wxString& shutdown, - const wxString& startup, const wxArrayString& command, - const wxArrayString& status, const wxArrayString& outputs + virtual void setBeacon(unsigned int time, const std::string& text, bool voice, TEXT_LANG language); + virtual void setAnnouncement(bool enabled, unsigned int time, const std::string& recordRPT1, const std::string& recordRPT2, const std::string& deleteRPT1, const std::string& deleteRPT2); + + virtual void setControl(bool enabled, const std::string& rpt1Callsign, + const std::string& rpt2Callsign, const std::string& shutdown, + const std::string& startup, const std::vector& command, + const std::vector& commandLine, + const std::vector& status, const std::vector& outputs ); virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); - virtual void setLogging(bool logging, const wxString& dir); + virtual void setLogging(bool logging, const std::string& dir); virtual void setWhiteList(CCallsignList* list); virtual void setBlackList(CCallsignList* list); virtual void setGreyList(CCallsignList* list); @@ -73,7 +84,7 @@ public: virtual CDStarRepeaterStatusData* getStatus(); - virtual void *Entry(); + virtual void entry(); virtual void kill(); @@ -84,112 +95,124 @@ public: virtual void transmitAnnouncementData(const unsigned char* data, unsigned int length, bool end); private: - wxString m_type; + std::string m_type; // Modem type string (e.g. "MMDVM"), used to special-case DVAP status CModem* m_modem; - CRepeaterProtocolHandler* m_protocolHandler; - CExternalController* m_controller; - bool m_stopped; - wxString m_rptCallsign; - wxString m_gwyCallsign; - CBeaconUnit* m_beacon; - CAnnouncementUnit* m_announcement; - wxString m_recordRPT1; - wxString m_recordRPT2; - wxString m_deleteRPT1; - wxString m_deleteRPT2; - CHeaderData* m_rxHeader; - COutputQueue m_localQueue; - COutputQueue m_radioQueue; - COutputQueue** m_networkQueue; - unsigned int m_writeNum; - unsigned int m_readNum; - unsigned char m_radioSeqNo; - unsigned char m_networkSeqNo; - unsigned char m_lastSlowDataType; - CTimer m_timeoutTimer; - CTimer m_watchdogTimer; - CTimer m_pollTimer; - CTimer m_ackTimer; - - CTimer m_statusAnnounceTimer[5]; - - CTimer m_beaconTimer; - CTimer m_announcementTimer; - - CTimer m_statusTimer; - - CTimer m_heartbeatTimer; - DSTAR_RPT_STATE m_rptState; - DSTAR_RX_STATE m_rxState; - CSlowDataDecoder m_slowDataDecoder; - CSlowDataEncoder m_ackEncoder; - CSlowDataEncoder m_linkEncoder; - CSlowDataEncoder m_headerEncoder; - - // XXX ARRAY! - CSlowDataEncoder m_status1Encoder; + CRepeaterProtocolHandler* m_protocolHandler; // UDP link to ircDDB/DStarGateway; null when no gateway is configured + CExternalController* m_controller; // PTT / active-indicator hardware + std::string m_rptCallsign; // Our 8-char RPT1 callsign (padded with spaces) + std::string m_gwyCallsign; // Gateway callsign (RPT2); defaults to rpt+G suffix + CBeaconUnit* m_beacon; // Synthesises periodic ID transmissions; null if beacons are off + CAnnouncementUnit* m_announcement; // Plays back a pre-recorded audio clip periodically; null if disabled + std::string m_recordRPT1; // RPT1/RPT2 callsign pair that triggers announcement recording + std::string m_recordRPT2; + std::string m_deleteRPT1; // RPT1/RPT2 pair that triggers announcement deletion + std::string m_deleteRPT2; + CHeaderData* m_rxHeader; // Header from the currently active transmission (RF or network) + + // Three output queues feed the modem. Priority: radio > local > network. + COutputQueue m_localQueue; // Beacon / announcement / ack / status transmissions + COutputQueue m_radioQueue; // RF → RF retransmit (duplex mode only) + COutputQueue** m_networkQueue; // Double-buffered network → RF queue (NETWORK_QUEUE_COUNT slots) + unsigned int m_writeNum; // Index of the queue slot currently being written by receiveNetwork() + unsigned int m_readNum; // Index of the slot currently being drained to the modem + + unsigned char m_radioSeqNo; // Frame sequence counter (0..20) for sync regeneration on the RF path + unsigned char m_networkSeqNo; // Frame sequence counter for the network path (used for gap filling) + unsigned char m_lastSlowDataType; // Tracks the previous slow-data type byte (gateway header substitution) + + // Timers — all clocked via clock() every ~9 ms + CTimer m_timeoutTimer; // Maximum on-air time per transmission; fires → DSRS_TIMEOUT + CTimer m_watchdogTimer; // Network data watchdog; fires if gateway stops sending frames + CTimer m_pollTimer; // Periodic gateway keepalive poll (60 s) + CTimer m_ackTimer; // Delay between end-of-transmission and the ack/status reply (0.5 s) + + CTimer m_statusAnnounceTimer[5]; // Per-status short delay before transmitting a user-status reply + + CTimer m_beaconTimer; // Interval between beacon transmissions (configurable, default 10 min) + CTimer m_announcementTimer; // Interval between announcement playbacks + + CTimer m_statusTimer; // Throttle for modem space/TX-state polling (100 ms) + CTimer m_heartbeatTimer; // 1 s pulse to drive the controller heartbeat output + + // Repeater state machine — the two axes of state + DSTAR_RPT_STATE m_rptState; // Overall repeater state: LISTENING / VALID / TIMEOUT / NETWORK / SHUTDOWN / … + DSTAR_RX_STATE m_rxState; // Radio receive state: LISTENING / PROCESS_SLOW_DATA / PROCESS_DATA + + CSlowDataDecoder m_slowDataDecoder; // Extracts embedded header from slow data (no fast-data header case) + CSlowDataEncoder m_ackEncoder; // Encodes BER / link text into the ack slow-data stream + CSlowDataEncoder m_linkEncoder; // Encodes link/error status for error-reply transmissions + CSlowDataEncoder m_headerEncoder; // Re-encodes the modified header into the gateway slow-data stream + + CSlowDataEncoder m_status1Encoder; // Encoders for the five user-defined status messages CSlowDataEncoder m_status2Encoder; CSlowDataEncoder m_status3Encoder; CSlowDataEncoder m_status4Encoder; CSlowDataEncoder m_status5Encoder; - // XXX ARRAY! - wxArrayString m_statusText; - - bool m_tx; - unsigned int m_space; - bool m_killed; - DSTAR_MODE m_mode; - ACK_TYPE m_ack; - bool m_restriction; - bool m_rpt1Validation; - bool m_errorReply; - bool m_controlEnabled; - wxString m_controlRPT1; - wxString m_controlRPT2; - wxString m_controlShutdown; - wxString m_controlStartup; - - wxArrayString m_controlStatus; - wxArrayString m_controlCommand; - - wxArrayString m_controlOutput; - - bool m_output[4]; - - CTimer m_activeHangTimer; - bool m_shutdown; - bool m_disable; - CDVTOOLFileWriter* m_logging; - unsigned char* m_lastData; - CAMBEFEC m_ambe; - unsigned int m_ambeFrames; - unsigned int m_ambeSilence; - unsigned int m_ambeBits; - unsigned int m_ambeErrors; - unsigned int m_lastAMBEBits; + std::vector m_statusText; // Five status strings received from the gateway + + bool m_tx; // Cached modem TX state (refreshed every 100 ms via m_statusTimer) + unsigned int m_space; // Cached modem buffer space (frames available to write) + std::atomic m_killed; // Set by kill() to break the entry() loop + DSTAR_MODE m_mode; // DUPLEX / SIMPLEX / GATEWAY — affects relay and ack behaviour + ACK_TYPE m_ack; // AT_NONE / AT_BER / AT_TEXT — what to send after a valid QSO + bool m_restriction; // When true, only callsigns matching the repeater prefix are allowed + bool m_rpt1Validation; // When false, simplex headers are re-addressed as repeater headers + bool m_errorReply; // When true, send an error-status reply to rejected transmissions + bool m_controlEnabled; // DTMF/YSFSF control is active + std::string m_controlRPT1; // RPT1 callsign that must match for a control command to be accepted + std::string m_controlRPT2; // RPT2 callsign that must match + std::string m_controlShutdown; // YOUR callsign that triggers a software shutdown + std::string m_controlStartup; // YOUR callsign that cancels a software shutdown + + std::vector m_controlStatus; // YOUR callsigns that trigger status announcements (up to 5) + std::vector m_controlCommand; // YOUR callsigns that trigger shell commands (up to 6) + std::vector m_controlCommandLine;// Shell command strings paired with m_controlCommand entries + + std::vector m_controlOutput; // YOUR callsigns that toggle physical output lines (up to 4) + + bool m_output[4]; // Current state of the four configurable output lines + + CTimer m_activeHangTimer; // Keeps the active indicator asserted briefly after the QSO ends + bool m_shutdown; // Set by shutdown() / cleared by startup() via DTMF or UI + bool m_disable; // Mirrors the hardware disable input from the controller + CDVTOOLFileWriter* m_logging; // Optional per-QSO DVTOOL frame log; null when logging is disabled + + // AMBE BER tracking — accumulated per transmission, reset on each new header + unsigned char* m_lastData; // Copy of the most recent network frame (used to fill gaps) + CAMBEFEC m_ambe; // AMBE FEC regenerator / bit-error counter + unsigned int m_ambeFrames; // Total voice frames received in this transmission + unsigned int m_ambeSilence; // Number of frames that were silence (null AMBE) + unsigned int m_ambeBits; // Cumulative FEC bits examined (denominator for BER) + unsigned int m_ambeErrors; // Cumulative FEC bit errors (numerator for BER) + unsigned int m_lastAMBEBits; // Snapshot taken at the last getStatus() call (for incremental BER) unsigned int m_lastAMBEErrors; - wxString m_ackText; - wxString m_tempAckText; - LINK_STATUS m_linkStatus; - wxString m_reflector; - - wxRegEx m_regEx; - wxStopWatch m_headerTime; - wxStopWatch m_packetTime; - unsigned int m_packetCount; - unsigned int m_packetSilence; - CCallsignList* m_whiteList; - CCallsignList* m_blackList; - CCallsignList* m_greyList; - bool m_blocked; - bool m_busyData; - bool m_blanking; - bool m_recording; - bool m_deleting; + + std::string m_ackText; // Slow-data text received from the gateway (e.g. reflector name) + std::string m_tempAckText; // One-shot override ack text; cleared after use + LINK_STATUS m_linkStatus; // Current reflector link state (used to format the BER ack string) + std::string m_reflector; // Name of the linked reflector, if any + + // Callsign validation regex: matches standard amateur callsigns (e.g. G4KLX, M0XYZ, VK3ABC) + std::regex m_regEx; + + // Packet timing for network gap detection — used to insert silence frames when UDP packets are late + std::chrono::steady_clock::time_point m_headerTime; // When the current network header arrived + std::chrono::steady_clock::time_point m_packetTime; // When the last network data packet arrived + unsigned int m_packetCount; // Total frames processed in this network transmission + unsigned int m_packetSilence; // Frames inserted as silence due to loss or gaps + + CCallsignList* m_whiteList; // If set, only callsigns in the list may access the repeater + CCallsignList* m_blackList; // If set, callsigns in the list are always rejected + CCallsignList* m_greyList; // If set, matching callsigns access RF but are blocked from the network + bool m_blocked; // True when the current user is on the grey list (local only) + bool m_busyData; // True when the repeater is in NETWORK state and an RF user is transmitting + bool m_blanking; // When true, DTMF tones in the audio stream are muted before retransmission + bool m_recording; // True while recording an announcement from the current RF transmission + bool m_deleting; // True while absorbing (discarding) a delete-announcement transmission #if defined(MQTT) - CTimer m_mqttStatusTimer; + CTimer m_mqttStatusTimer; // 1 s timer driving MQTT status/BER publication #endif void receiveHeader(CHeaderData* header); diff --git a/DStarRepeater/DStarRepeaterTXRXThread.cpp b/DStarRepeater/DStarRepeaterTXRXThread.cpp index 0b80aa6..3a010f9 100644 --- a/DStarRepeater/DStarRepeaterTXRXThread.cpp +++ b/DStarRepeater/DStarRepeaterTXRXThread.cpp @@ -22,10 +22,12 @@ #include "DStarDefines.h" #include "HeaderData.h" #include "Version.h" +#include "Logger.h" #include "Utils.h" -const unsigned char DTMF_MASK[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U}; -const unsigned char DTMF_SIG[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; +#include + +using namespace std::chrono; const unsigned int MAX_DATA_SYNC_BIT_ERRS = 2U; @@ -35,15 +37,15 @@ const unsigned int SILENCE_THRESHOLD = 2U; const unsigned int CYCLE_TIME = 9U; -CDStarRepeaterTXRXThread::CDStarRepeaterTXRXThread(const wxString& type) : +CDStarRepeaterTXRXThread::CDStarRepeaterTXRXThread(const std::string& type) : m_type(type), -m_modem(NULL), -m_protocolHandler(NULL), -m_controller(NULL), +m_modem(nullptr), +m_protocolHandler(nullptr), +m_controller(nullptr), m_rptCallsign(), -m_rxHeader(NULL), -m_txHeader(NULL), -m_networkQueue(NULL), +m_rxHeader(nullptr), +m_txHeader(nullptr), +m_networkQueue(nullptr), m_writeNum(0U), m_readNum(0U), m_radioSeqNo(0U), @@ -61,7 +63,7 @@ m_space(0U), m_killed(false), m_activeHangTimer(1000U), m_disable(false), -m_lastData(NULL), +m_lastData(nullptr), m_ambe(), m_ambeFrames(0U), m_ambeSilence(0U), @@ -95,16 +97,35 @@ CDStarRepeaterTXRXThread::~CDStarRepeaterTXRXThread() delete[] m_lastData; delete m_rxHeader; delete m_txHeader; -} - -void *CDStarRepeaterTXRXThread::Entry() + delete m_modem; + delete m_controller; + delete m_protocolHandler; +} + +// --------------------------------------------------------------------------- +// entry() — split-site TX+RX thread body. +// +// Requires modem, controller, protocol handler, and callsign before starting. +// Main loop (~9 ms): +// 1. Refresh modem space/TX state every 100 ms. +// 2. receiveModem() — forward RF audio to the network (no local retransmit). +// 3. receiveNetwork() — queue gateway audio for RF transmit. +// 4. repeaterStateMachine()— network watchdog timeout → force EOT. +// 5. Gateway register timer (keepalive every 30 s). +// 6. Heartbeat output to the controller (1 s). +// 7. MQTT status (1 s). +// 8. Active-indicator: follows m_tx or the active-hang timer. +// 9. Disable/shutdown handling (hardware disable line only; no software shutdown). +// 10. Drain the network queue to the modem. +// --------------------------------------------------------------------------- +void CDStarRepeaterTXRXThread::entry() { // Wait here until we have the essentials to run - while (!m_killed && (m_modem == NULL || m_controller == NULL || m_protocolHandler == NULL || m_rptCallsign.IsEmpty() || m_rptCallsign.IsSameAs(wxT(" ")))) - ::wxMilliSleep(500UL); // 1/2 sec + while (!m_killed && (m_modem == nullptr || m_controller == nullptr || m_protocolHandler == nullptr || m_rptCallsign.empty() || m_rptCallsign == " ")) + std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 1/2 sec if (m_killed) - return NULL; + return; m_controller->setActive(false); m_controller->setRadioTransmit(false); @@ -116,18 +137,13 @@ void *CDStarRepeaterTXRXThread::Entry() m_mqttStatusTimer.start(); #endif - wxString hardware = m_type; - int n = hardware.Find(wxT(' ')); - if (n != wxNOT_FOUND) - hardware = m_type.Left(n); - - wxLogMessage(wxT("Starting the D-Star transmitter and receiver thread")); + wxLogMessage("Starting the D-Star transmitter and receiver thread"); - wxStopWatch stopWatch; + auto stopWatch = steady_clock::now(); try { while (!m_killed) { - stopWatch.Start(); + stopWatch = steady_clock::now(); if (m_statusTimer.hasExpired() || m_space == 0U) { m_space = m_modem->getSpace(); @@ -156,7 +172,7 @@ void *CDStarRepeaterTXRXThread::Entry() #if defined(MQTT) // Publish status to MQTT every second if (m_mqttStatusTimer.hasExpired()) { - if (g_mqtt != NULL) { + if (g_mqtt != nullptr) { CDStarRepeaterStatusData* status = getStatus(); std::string json = status->toJSON(); g_mqtt->publish("status", json.c_str()); @@ -210,36 +226,37 @@ void *CDStarRepeaterTXRXThread::Entry() m_controller->setRadioTransmit(m_tx); - unsigned long ms = stopWatch.Time(); + long long ms = duration_cast(steady_clock::now() - stopWatch).count(); if (ms < CYCLE_TIME) { - ::wxMilliSleep(CYCLE_TIME - ms); + std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_TIME - ms)); clock(CYCLE_TIME); } else { - clock(ms); + clock((unsigned int)ms); } } } catch (std::exception& e) { - wxString message(e.what(), wxConvLocal); - wxLogError(wxT("Exception raised - \"%s\""), message.c_str()); + wxLogError("Exception raised - \"%s\"", e.what()); } catch (...) { - wxLogError(wxT("Unknown exception raised")); + wxLogError("Unknown exception raised"); } - wxLogMessage(wxT("Stopping the D-Star transmitter and receiver thread")); + wxLogMessage("Stopping the D-Star transmitter and receiver thread"); m_modem->stop(); + delete m_modem; + m_modem = nullptr; m_controller->setActive(false); m_controller->setRadioTransmit(false); m_controller->close(); delete m_controller; + m_controller = nullptr; m_protocolHandler->close(); delete m_protocolHandler; - - return NULL; + m_protocolHandler = nullptr; } void CDStarRepeaterTXRXThread::kill() @@ -247,21 +264,21 @@ void CDStarRepeaterTXRXThread::kill() m_killed = true; } -void CDStarRepeaterTXRXThread::setCallsign(const wxString& callsign, const wxString&, DSTAR_MODE, ACK_TYPE, bool, bool, bool, bool) +void CDStarRepeaterTXRXThread::setCallsign(const std::string& callsign, const std::string&, DSTAR_MODE, ACK_TYPE, bool, bool, bool, bool) { // Pad the callsign up to eight characters m_rptCallsign = callsign; - m_rptCallsign.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + m_rptCallsign.resize(LONG_CALLSIGN_LENGTH, ' '); } void CDStarRepeaterTXRXThread::setProtocolHandler(CRepeaterProtocolHandler* handler, bool local) { - wxASSERT(handler != NULL); + assert(handler != nullptr); m_protocolHandler = handler; if (local) { - wxLogInfo(wxT("Reducing transmit buffering because of local connection")); + wxLogInfo("Reducing transmit buffering because of local connection"); for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) m_networkQueue[i]->setThreshold(LOCAL_RUN_FRAME_COUNT); @@ -270,7 +287,7 @@ void CDStarRepeaterTXRXThread::setProtocolHandler(CRepeaterProtocolHandler* hand void CDStarRepeaterTXRXThread::setModem(CModem* modem) { - wxASSERT(modem != NULL); + assert(modem != nullptr); m_modem = modem; } @@ -279,17 +296,17 @@ void CDStarRepeaterTXRXThread::setTimes(unsigned int, unsigned int) { } -void CDStarRepeaterTXRXThread::setBeacon(unsigned int, const wxString&, bool, TEXT_LANG) +void CDStarRepeaterTXRXThread::setBeacon(unsigned int, const std::string&, bool, TEXT_LANG) { } -void CDStarRepeaterTXRXThread::setAnnouncement(bool, unsigned int, const wxString&, const wxString&, const wxString&, const wxString&) +void CDStarRepeaterTXRXThread::setAnnouncement(bool, unsigned int, const std::string&, const std::string&, const std::string&, const std::string&) { } void CDStarRepeaterTXRXThread::setController(CExternalController* controller, unsigned int activeHangTime) { - wxASSERT(controller != NULL); + assert(controller != nullptr); m_controller = controller; m_activeHangTimer.setTimeout(activeHangTime); @@ -299,21 +316,15 @@ void CDStarRepeaterTXRXThread::setOutputs(bool, bool, bool, bool) { } -void CDStarRepeaterTXRXThread::setLogging(bool, const wxString&) +void CDStarRepeaterTXRXThread::setLogging(bool, const std::string&) { } -void CDStarRepeaterTXRXThread::setWhiteList(CCallsignList*) -{ -} +void CDStarRepeaterTXRXThread::setWhiteList(CCallsignList* list) { delete list; } -void CDStarRepeaterTXRXThread::setBlackList(CCallsignList*) -{ -} +void CDStarRepeaterTXRXThread::setBlackList(CCallsignList* list) { delete list; } -void CDStarRepeaterTXRXThread::setGreyList(CCallsignList*) -{ -} +void CDStarRepeaterTXRXThread::setGreyList(CCallsignList* list) { delete list; } void CDStarRepeaterTXRXThread::receiveModem() { @@ -368,9 +379,9 @@ void CDStarRepeaterTXRXThread::receiveModem() void CDStarRepeaterTXRXThread::receiveHeader(CHeaderData* header) { - wxASSERT(header != NULL); + assert(header != nullptr); - wxLogMessage(wxT("Radio header decoded - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Radio header decoded - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); bool res = processRadioHeader(header); if (res) { @@ -379,7 +390,7 @@ void CDStarRepeaterTXRXThread::receiveHeader(CHeaderData* header) setRadioState(DSRXS_PROCESS_DATA); } else { // This is a DD packet or some other problem - // wxLogMessage(wxT("Invalid header")); + // wxLogMessage("Invalid header"); } } @@ -392,11 +403,11 @@ void CDStarRepeaterTXRXThread::receiveSlowData(unsigned char* data, unsigned int // The data sync has been seen, a fuzzy match is used, two bit errors or less if (errs <= MAX_DATA_SYNC_BIT_ERRS) { - // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + // wxLogMessage("Found data sync at frame %u, errs: %u", m_radioSeqNo, errs); m_radioSeqNo = 0U; m_slowDataDecoder.sync(); } else if (m_radioSeqNo == 20U) { - // wxLogMessage(wxT("Assuming data sync")); + // wxLogMessage("Assuming data sync"); m_radioSeqNo = 0U; m_slowDataDecoder.sync(); } else { @@ -404,20 +415,18 @@ void CDStarRepeaterTXRXThread::receiveSlowData(unsigned char* data, unsigned int m_slowDataDecoder.addData(data + VOICE_FRAME_LENGTH_BYTES); CHeaderData* header = m_slowDataDecoder.getHeaderData(); - if (header == NULL) + if (header == nullptr) return; - wxLogMessage(wxT("Radio header from slow data - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X BER: 0%%"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Radio header from slow data - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X BER: 0%%", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); - if (header != NULL) { - bool res = processRadioHeader(header); - if (res) { - // A valid header and is a DV packet, go to normal data relaying - setRadioState(DSRXS_PROCESS_DATA); - } else { - // This is a DD packet or some other problem - // wxLogMessage(wxT("Invalid header")); - } + bool res = processRadioHeader(header); + if (res) { + // A valid header and is a DV packet, go to normal data relaying + setRadioState(DSRXS_PROCESS_DATA); + } else { + // This is a DD packet or some other problem + // wxLogMessage("Invalid header"); } } } @@ -431,11 +440,11 @@ void CDStarRepeaterTXRXThread::receiveRadioData(unsigned char* data, unsigned in // The data sync has been seen, a fuzzy match is used, two bit errors or less if (errs <= MAX_DATA_SYNC_BIT_ERRS) { - // wxLogMessage(wxT("Found data sync at frame %u, errs: %u"), m_radioSeqNo, errs); + // wxLogMessage("Found data sync at frame %u, errs: %u", m_radioSeqNo, errs); m_radioSeqNo = 0U; processRadioFrame(data, FRAME_SYNC); } else if (m_radioSeqNo == 20U) { - // wxLogMessage(wxT("Regenerating data sync")); + // wxLogMessage("Regenerating data sync"); m_radioSeqNo = 0U; processRadioFrame(data, FRAME_SYNC); } else { @@ -456,13 +465,13 @@ void CDStarRepeaterTXRXThread::receiveNetwork() break; } else if (type == NETWORK_HEADER) { // A header CHeaderData* header = m_protocolHandler->readHeader(); - if (header != NULL) { + if (header != nullptr) { ::memcpy(m_lastData, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); processNetworkHeader(header); - m_headerTime.Start(); - m_packetTime.Start(); + m_headerTime = steady_clock::now(); + m_packetTime = steady_clock::now(); m_packetCount = 0U; m_packetSilence = 0U; } @@ -481,10 +490,10 @@ void CDStarRepeaterTXRXThread::receiveNetwork() } // Have we missed any data frames? - if (m_transmitting && m_packetTime.Time() > 200L) { - unsigned int packetsNeeded = m_headerTime.Time() / DSTAR_FRAME_TIME_MS; - - // wxLogMessage(wxT("Time: %u ms, need %u packets and received %u packets"), ms - m_headerMS, packetsNeeded, m_packetCount); + long long packetMs = duration_cast(steady_clock::now() - m_packetTime).count(); + if (m_transmitting && packetMs > 200L) { + long long headerMs = duration_cast(steady_clock::now() - m_headerTime).count(); + unsigned int packetsNeeded = (unsigned int)(headerMs / DSTAR_FRAME_TIME_MS); if (packetsNeeded > m_packetCount) { unsigned int count = packetsNeeded - m_packetCount; @@ -492,8 +501,6 @@ void CDStarRepeaterTXRXThread::receiveNetwork() if (count > 5U) { count -= 2U; - // wxLogMessage(wxT("Inserting %u silence frames into the network data stream"), count); - // Create silence frames for (unsigned int i = 0U; i < count; i++) { unsigned char data[DV_FRAME_LENGTH_BYTES]; @@ -504,13 +511,13 @@ void CDStarRepeaterTXRXThread::receiveNetwork() } } - m_packetTime.Start(); + m_packetTime = steady_clock::now(); } } void CDStarRepeaterTXRXThread::transmitNetworkHeader(const CHeaderData& header) { - wxLogMessage(wxT("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header.getMyCall1().c_str(), header.getMyCall2().c_str(), header.getYourCall().c_str(), header.getRptCall1().c_str(), header.getRptCall2().c_str(), header.getFlag1(), header.getFlag2(), header.getFlag3()); + wxLogMessage("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header.getMyCall1().c_str(), header.getMyCall2().c_str(), header.getYourCall().c_str(), header.getRptCall1().c_str(), header.getRptCall2().c_str(), header.getFlag1(), header.getFlag2(), header.getFlag3()); bool empty = m_networkQueue[m_readNum]->isEmpty(); if (!empty) { @@ -541,7 +548,7 @@ void CDStarRepeaterTXRXThread::transmitNetworkHeader() return; CHeaderData* header = m_networkQueue[m_readNum]->getHeader(); - if (header == NULL) + if (header == nullptr) return; m_modem->writeHeader(*header); @@ -575,7 +582,7 @@ void CDStarRepeaterTXRXThread::transmitNetworkData() void CDStarRepeaterTXRXThread::repeaterStateMachine() { if (m_watchdogTimer.isRunning() && m_watchdogTimer.hasExpired()) { - wxLogMessage(wxT("Network watchdog has expired")); + wxLogMessage("Network watchdog has expired"); // Send end of transmission data to the radio m_networkQueue[m_writeNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); #if defined(MQTT) @@ -588,7 +595,7 @@ void CDStarRepeaterTXRXThread::repeaterStateMachine() void CDStarRepeaterTXRXThread::setRadioState(DSTAR_RX_STATE state) { - // This is the too state + // This is the to state switch (state) { case DSRXS_LISTENING: m_rxState = DSRXS_LISTENING; @@ -649,11 +656,11 @@ bool CDStarRepeaterTXRXThread::setRepeaterState(DSTAR_RPT_STATE state) bool CDStarRepeaterTXRXThread::processRadioHeader(CHeaderData* header) { - wxASSERT(header != NULL); + assert(header != nullptr); // We don't handle DD data packets if (header->isDataPacket()) { - wxLogMessage(wxT("Received a DD packet, ignoring")); + wxLogMessage("Received a DD packet, ignoring"); delete header; return false; } @@ -684,7 +691,7 @@ bool CDStarRepeaterTXRXThread::processRadioHeader(CHeaderData* header) void CDStarRepeaterTXRXThread::processNetworkHeader(CHeaderData* header) { - wxASSERT(header != NULL); + assert(header != nullptr); // If shutdown we ignore incoming headers if (m_rptState == DSRS_SHUTDOWN) { @@ -692,11 +699,11 @@ void CDStarRepeaterTXRXThread::processNetworkHeader(CHeaderData* header) return; } - wxLogMessage(wxT("Network header received - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Network header received - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); // Is it for us? - if (!header->getRptCall2().IsSameAs(m_rptCallsign)) { - wxLogMessage(wxT("Invalid network RPT2 value, ignoring")); + if (header->getRptCall2() != m_rptCallsign) { + wxLogMessage("Invalid network RPT2 value, ignoring"); delete header; return; } @@ -757,8 +764,8 @@ void CDStarRepeaterTXRXThread::processRadioFrame(unsigned char* data, FRAME_TYPE unsigned int CDStarRepeaterTXRXThread::processNetworkFrame(unsigned char* data, unsigned int length, unsigned char seqNo) { - wxASSERT(data != NULL); - wxASSERT(length > 0U); + assert(data != nullptr); + assert(length > 0U); // If shutdown we ignore incoming data if (m_rptState == DSRS_SHUTDOWN) @@ -839,7 +846,7 @@ unsigned int CDStarRepeaterTXRXThread::processNetworkFrame(unsigned char* data, void CDStarRepeaterTXRXThread::endOfRadioData() { - wxLogMessage(wxT("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%"), m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); + wxLogMessage("AMBE for %s Frames: %.1fs, Silence: %.1f%%, BER: %.1f%%", m_rxHeader->getMyCall1().c_str(), float(m_ambeFrames) / 50.0F, float(m_ambeSilence * 100U) / float(m_ambeFrames), float(m_ambeErrors * 100U) / float(m_ambeBits)); setRepeaterState(DSRS_LISTENING); } @@ -850,7 +857,7 @@ void CDStarRepeaterTXRXThread::endOfNetworkData() if (m_packetCount != 0U) loss = float(m_packetSilence) / float(m_packetCount); - wxLogMessage(wxT("Stats for %s Frames: %.1fs, Loss: %.1f%%, Packets: %u/%u"), m_txHeader->getMyCall1().c_str(), float(m_packetCount) / 50.0F, loss * 100.0F, m_packetSilence, m_packetCount); + wxLogMessage("Stats for %s Frames: %.1fs, Loss: %.1f%%, Packets: %u/%u", m_txHeader->getMyCall1().c_str(), float(m_packetCount) / 50.0F, loss * 100.0F, m_packetSilence, m_packetCount); m_watchdogTimer.stop(); m_activeHangTimer.start(); @@ -874,17 +881,17 @@ CDStarRepeaterStatusData* CDStarRepeaterTXRXThread::getStatus() CDStarRepeaterStatusData* status; if (m_rptState == DSRS_SHUTDOWN || m_rptState == DSRS_LISTENING) - status = new CDStarRepeaterStatusData(wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, - wxEmptyString, 0x00, 0x00, 0x00, m_tx, m_rxState, m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, 0.0F, - wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString); + status = new CDStarRepeaterStatusData(std::string(), std::string(), std::string(), std::string(), + std::string(), 0x00, 0x00, 0x00, m_tx, m_rxState, m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, 0.0F, + std::string(), std::string(), std::string(), std::string(), std::string(), std::string()); else status = new CDStarRepeaterStatusData(m_rxHeader->getMyCall1(), m_rxHeader->getMyCall2(), m_rxHeader->getYourCall(), m_rxHeader->getRptCall1(), m_rxHeader->getRptCall2(), m_rxHeader->getFlag1(), m_rxHeader->getFlag2(), m_rxHeader->getFlag3(), m_tx, m_rxState, - m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, (errors * 100.0F) / bits, wxEmptyString, wxEmptyString, - wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString); + m_rptState, 0U, 0U, 0U, 0U, 0U, 0U, (errors * 100.0F) / bits, std::string(), std::string(), + std::string(), std::string(), std::string(), std::string()); - if (m_type.IsSameAs(wxT("DVAP")) && m_modem != NULL) { + if (m_type == "DVAP" && m_modem != nullptr) { CDVAPController* dvap = static_cast(m_modem); bool squelch = dvap->getSquelch(); int signal = dvap->getSignal(); @@ -914,30 +921,6 @@ void CDStarRepeaterTXRXThread::startup() { } -void CDStarRepeaterTXRXThread::command1() -{ -} - -void CDStarRepeaterTXRXThread::command2() -{ -} - -void CDStarRepeaterTXRXThread::command3() -{ -} - -void CDStarRepeaterTXRXThread::command4() -{ -} - -void CDStarRepeaterTXRXThread::command5() -{ -} - -void CDStarRepeaterTXRXThread::command6() -{ -} - unsigned int CDStarRepeaterTXRXThread::countBits(unsigned char byte) { unsigned int bits = 0U; diff --git a/DStarRepeater/DStarRepeaterTXRXThread.h b/DStarRepeater/DStarRepeaterTXRXThread.h index 51b6daf..ad9be27 100644 --- a/DStarRepeater/DStarRepeaterTXRXThread.h +++ b/DStarRepeater/DStarRepeaterTXRXThread.h @@ -34,23 +34,38 @@ #include "MQTTPublisher.h" #endif -#include - +#include "StdCompat.h" +#include +#include + +// CDStarRepeaterTXRXThread — split-site thread (MODE_TXANDRX). +// +// Used when the transmitter and receiver are physically separate (e.g. a +// linked-site repeater connected over a network via the Split modem driver). +// Both RF paths exist but they are independent: the receive side forwards to +// the network without local RF retransmission, and the transmit side plays +// back network audio without echoing RF. +// +// The state machine is a lightweight subset of TRXThread: +// LISTENING ↔ VALID (radio path) +// LISTENING ↔ NETWORK (network path, guarded by watchdog) +// No ack, no timeout, no beacon, no announcement, no control commands. +// The m_transmitting flag tracks whether a network stream is actively playing. class CDStarRepeaterTXRXThread : public IDStarRepeaterThread { public: - CDStarRepeaterTXRXThread(const wxString& type); + CDStarRepeaterTXRXThread(const std::string& type); virtual ~CDStarRepeaterTXRXThread(); - virtual void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); + virtual void setCallsign(const std::string& callsign, const std::string& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); virtual void setProtocolHandler(CRepeaterProtocolHandler* handler, bool local); virtual void setModem(CModem* modem); virtual void setController(CExternalController* controller, unsigned int activeHangTime); virtual void setTimes(unsigned int timeout, unsigned int ackTime); - virtual void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language); - virtual void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); + virtual void setBeacon(unsigned int time, const std::string& text, bool voice, TEXT_LANG language); + virtual void setAnnouncement(bool enabled, unsigned int time, const std::string& recordRPT1, const std::string& recordRPT2, const std::string& deleteRPT1, const std::string& deleteRPT2); virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); - virtual void setLogging(bool logging, const wxString& dir); + virtual void setLogging(bool logging, const std::string& dir); virtual void setWhiteList(CCallsignList* list); virtual void setBlackList(CCallsignList* list); virtual void setGreyList(CCallsignList* list); @@ -58,25 +73,18 @@ public: virtual void shutdown(); virtual void startup(); - virtual void command1(); - virtual void command2(); - virtual void command3(); - virtual void command4(); - virtual void command5(); - virtual void command6(); - virtual CDStarRepeaterStatusData* getStatus(); virtual void kill(); - virtual void *Entry(); + virtual void entry(); private: - wxString m_type; + std::string m_type; CModem* m_modem; CRepeaterProtocolHandler* m_protocolHandler; CExternalController* m_controller; - wxString m_rptCallsign; + std::string m_rptCallsign; CHeaderData* m_rxHeader; CHeaderData* m_txHeader; COutputQueue** m_networkQueue; @@ -94,7 +102,7 @@ private: bool m_tx; bool m_transmitting; unsigned int m_space; - bool m_killed; + std::atomic m_killed; CTimer m_activeHangTimer; bool m_disable; unsigned char* m_lastData; @@ -105,8 +113,8 @@ private: unsigned int m_ambeErrors; unsigned int m_lastAMBEBits; unsigned int m_lastAMBEErrors; - wxStopWatch m_headerTime; - wxStopWatch m_packetTime; + std::chrono::steady_clock::time_point m_headerTime; + std::chrono::steady_clock::time_point m_packetTime; unsigned int m_packetCount; unsigned int m_packetSilence; diff --git a/DStarRepeater/DStarRepeaterTXThread.cpp b/DStarRepeater/DStarRepeaterTXThread.cpp index 63bedde..16bfd28 100644 --- a/DStarRepeater/DStarRepeaterTXThread.cpp +++ b/DStarRepeater/DStarRepeaterTXThread.cpp @@ -21,9 +21,11 @@ #include "DStarDefines.h" #include "HeaderData.h" #include "Version.h" +#include "Logger.h" -const unsigned char DTMF_MASK[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U}; -const unsigned char DTMF_SIG[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; +#include + +using namespace std::chrono; const unsigned int MAX_DATA_SYNC_BIT_ERRS = 2U; @@ -33,14 +35,13 @@ const unsigned int SILENCE_THRESHOLD = 2U; const unsigned int CYCLE_TIME = 9U; -CDStarRepeaterTXThread::CDStarRepeaterTXThread(const wxString& type) : +CDStarRepeaterTXThread::CDStarRepeaterTXThread(const std::string& type) : m_type(type), -m_modem(NULL), -m_protocolHandler(NULL), -m_stopped(true), +m_modem(nullptr), +m_protocolHandler(nullptr), m_rptCallsign(), -m_txHeader(NULL), -m_networkQueue(NULL), +m_txHeader(nullptr), +m_networkQueue(nullptr), m_writeNum(0U), m_readNum(0U), m_networkSeqNo(0U), @@ -51,7 +52,7 @@ m_state(DSRS_LISTENING), m_tx(false), m_space(0U), m_killed(false), -m_lastData(NULL), +m_lastData(nullptr), m_ambe(), m_headerTime(), m_packetTime(), @@ -75,18 +76,31 @@ CDStarRepeaterTXThread::~CDStarRepeaterTXThread() delete[] m_networkQueue; delete[] m_lastData; delete m_txHeader; -} - -void *CDStarRepeaterTXThread::Entry() + delete m_modem; + delete m_protocolHandler; +} + +// --------------------------------------------------------------------------- +// entry() — transmit-only thread body. +// +// Requires modem, protocol handler, and callsign before starting. +// Main loop (~9 ms): +// 1. Refresh modem space/TX state every 100 ms. +// 2. receiveNetwork() — drain and queue gateway audio frames; handle gap filling. +// 3. receiveModem() — drain modem events and discard them (TX-only). +// 4. Network watchdog: if DSRS_NETWORK and no frames for NETWORK_TIMEOUT, force EOT. +// 5. Gateway register timer (keepalive every 30 s). +// 6. Drain the network queue to the modem when space is available. +// 7. MQTT status publication (1 s). +// --------------------------------------------------------------------------- +void CDStarRepeaterTXThread::entry() { // Wait here until we have the essentials to run - while (!m_killed && (m_modem == NULL || m_protocolHandler == NULL || m_rptCallsign.IsEmpty() || m_rptCallsign.IsSameAs(wxT(" ")))) - ::wxMilliSleep(500UL); // 1/2 sec + while (!m_killed && (m_modem == nullptr || m_protocolHandler == nullptr || m_rptCallsign.empty() || m_rptCallsign == " ")) + std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 1/2 sec if (m_killed) - return NULL; - - m_stopped = false; + return; m_registerTimer.start(10U); m_statusTimer.start(); @@ -94,18 +108,13 @@ void *CDStarRepeaterTXThread::Entry() m_mqttStatusTimer.start(); #endif - wxString hardware = m_type; - int n = hardware.Find(wxT(' ')); - if (n != wxNOT_FOUND) - hardware = m_type.Left(n); - - wxLogMessage(wxT("Starting the D-Star transmitter thread")); + wxLogMessage("Starting the D-Star transmitter thread"); - wxStopWatch stopWatch; + auto stopWatch = steady_clock::now(); try { while (!m_killed) { - stopWatch.Start(); + stopWatch = steady_clock::now(); if (m_statusTimer.hasExpired() || m_space == 0U) { m_space = m_modem->getSpace(); @@ -119,7 +128,7 @@ void *CDStarRepeaterTXThread::Entry() if (m_state == DSRS_NETWORK) { if (m_watchdogTimer.hasExpired()) { - wxLogMessage(wxT("Network watchdog has expired")); + wxLogMessage("Network watchdog has expired"); // Send end of transmission data to the radio m_networkQueue[m_writeNum]->addData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true); #if defined(MQTT) @@ -144,7 +153,7 @@ void *CDStarRepeaterTXThread::Entry() #if defined(MQTT) // Publish status to MQTT every second if (m_mqttStatusTimer.hasExpired()) { - if (g_mqtt != NULL) { + if (g_mqtt != nullptr) { CDStarRepeaterStatusData* status = getStatus(); std::string json = status->toJSON(); g_mqtt->publish("status", json.c_str()); @@ -154,31 +163,31 @@ void *CDStarRepeaterTXThread::Entry() } #endif - unsigned long ms = stopWatch.Time(); + long long ms = duration_cast(steady_clock::now() - stopWatch).count(); if (ms < CYCLE_TIME) { - ::wxMilliSleep(CYCLE_TIME - ms); + std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_TIME - ms)); clock(CYCLE_TIME); } else { - clock(ms); + clock((unsigned int)ms); } } } catch (std::exception& e) { - wxString message(e.what(), wxConvLocal); - wxLogError(wxT("Exception raised - \"%s\""), message.c_str()); + wxLogError("Exception raised - \"%s\"", e.what()); } catch (...) { - wxLogError(wxT("Unknown exception raised")); + wxLogError("Unknown exception raised"); } - wxLogMessage(wxT("Stopping the D-Star transmitter thread")); + wxLogMessage("Stopping the D-Star transmitter thread"); m_modem->stop(); + delete m_modem; + m_modem = nullptr; m_protocolHandler->close(); delete m_protocolHandler; - - return NULL; + m_protocolHandler = nullptr; } void CDStarRepeaterTXThread::kill() @@ -186,21 +195,21 @@ void CDStarRepeaterTXThread::kill() m_killed = true; } -void CDStarRepeaterTXThread::setCallsign(const wxString& callsign, const wxString&, DSTAR_MODE, ACK_TYPE, bool, bool, bool, bool) +void CDStarRepeaterTXThread::setCallsign(const std::string& callsign, const std::string&, DSTAR_MODE, ACK_TYPE, bool, bool, bool, bool) { // Pad the callsign up to eight characters m_rptCallsign = callsign; - m_rptCallsign.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + m_rptCallsign.resize(LONG_CALLSIGN_LENGTH, ' '); } void CDStarRepeaterTXThread::setProtocolHandler(CRepeaterProtocolHandler* handler, bool local) { - wxASSERT(handler != NULL); + assert(handler != nullptr); m_protocolHandler = handler; if (local) { - wxLogInfo(wxT("Reducing transmit buffering because of local connection")); + wxLogInfo("Reducing transmit buffering because of local connection"); for (unsigned int i = 0U; i < NETWORK_QUEUE_COUNT; i++) m_networkQueue[i]->setThreshold(LOCAL_RUN_FRAME_COUNT); @@ -209,7 +218,7 @@ void CDStarRepeaterTXThread::setProtocolHandler(CRepeaterProtocolHandler* handle void CDStarRepeaterTXThread::setModem(CModem* modem) { - wxASSERT(modem != NULL); + assert(modem != nullptr); m_modem = modem; } @@ -218,11 +227,11 @@ void CDStarRepeaterTXThread::setTimes(unsigned int, unsigned int) { } -void CDStarRepeaterTXThread::setBeacon(unsigned int, const wxString&, bool, TEXT_LANG) +void CDStarRepeaterTXThread::setBeacon(unsigned int, const std::string&, bool, TEXT_LANG) { } -void CDStarRepeaterTXThread::setAnnouncement(bool, unsigned int, const wxString&, const wxString&, const wxString&, const wxString&) +void CDStarRepeaterTXThread::setAnnouncement(bool, unsigned int, const std::string&, const std::string&, const std::string&, const std::string&) { } @@ -234,22 +243,17 @@ void CDStarRepeaterTXThread::setOutputs(bool, bool, bool, bool) { } -void CDStarRepeaterTXThread::setLogging(bool, const wxString&) +void CDStarRepeaterTXThread::setLogging(bool, const std::string&) { } -void CDStarRepeaterTXThread::setWhiteList(CCallsignList*) -{ -} +void CDStarRepeaterTXThread::setWhiteList(CCallsignList* list) { delete list; } -void CDStarRepeaterTXThread::setBlackList(CCallsignList*) -{ -} +void CDStarRepeaterTXThread::setBlackList(CCallsignList* list) { delete list; } -void CDStarRepeaterTXThread::setGreyList(CCallsignList*) -{ -} +void CDStarRepeaterTXThread::setGreyList(CCallsignList* list) { delete list; } +// Drain and discard all modem events — this thread does not receive from RF. void CDStarRepeaterTXThread::receiveModem() { for (;;) { @@ -271,13 +275,13 @@ void CDStarRepeaterTXThread::receiveNetwork() break; } else if (type == NETWORK_HEADER) { // A header CHeaderData* header = m_protocolHandler->readHeader(); - if (header != NULL) { + if (header != nullptr) { ::memcpy(m_lastData, NULL_FRAME_DATA_BYTES, DV_FRAME_LENGTH_BYTES); processNetworkHeader(header); - m_headerTime.Start(); - m_packetTime.Start(); + m_headerTime = steady_clock::now(); + m_packetTime = steady_clock::now(); m_packetCount = 0U; m_packetSilence = 0U; } @@ -296,10 +300,10 @@ void CDStarRepeaterTXThread::receiveNetwork() } // Have we missed any data frames? - if (m_state == DSRS_NETWORK && m_packetTime.Time() > 200L) { - unsigned int packetsNeeded = m_headerTime.Time() / DSTAR_FRAME_TIME_MS; - - // wxLogMessage(wxT("Time: %u ms, need %u packets and received %u packets"), ms - m_headerMS, packetsNeeded, m_packetCount); + long long packetMs = duration_cast(steady_clock::now() - m_packetTime).count(); + if (m_state == DSRS_NETWORK && packetMs > 200L) { + long long headerMs = duration_cast(steady_clock::now() - m_headerTime).count(); + unsigned int packetsNeeded = (unsigned int)(headerMs / DSTAR_FRAME_TIME_MS); if (packetsNeeded > m_packetCount) { unsigned int count = packetsNeeded - m_packetCount; @@ -307,8 +311,6 @@ void CDStarRepeaterTXThread::receiveNetwork() if (count > 5U) { count -= 2U; - // wxLogMessage(wxT("Inserting %u silence frames into the network data stream"), count); - // Create silence frames for (unsigned int i = 0U; i < count; i++) { unsigned char data[DV_FRAME_LENGTH_BYTES]; @@ -319,13 +321,13 @@ void CDStarRepeaterTXThread::receiveNetwork() } } - m_packetTime.Start(); + m_packetTime = steady_clock::now(); } } void CDStarRepeaterTXThread::transmitNetworkHeader(CHeaderData* header) { - wxLogMessage(wxT("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Transmitting to - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); bool empty = m_networkQueue[m_readNum]->isEmpty(); if (!empty) { @@ -356,7 +358,7 @@ void CDStarRepeaterTXThread::transmitNetworkHeader() return; CHeaderData* header = m_networkQueue[m_readNum]->getHeader(); - if (header == NULL) + if (header == nullptr) return; m_modem->writeHeader(*header); @@ -389,13 +391,13 @@ void CDStarRepeaterTXThread::transmitNetworkData() void CDStarRepeaterTXThread::processNetworkHeader(CHeaderData* header) { - wxASSERT(header != NULL); + assert(header != nullptr); - wxLogMessage(wxT("Network header received - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + wxLogMessage("Network header received - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); // Is it for us? - if (!header->getRptCall2().IsSameAs(m_rptCallsign)) { - wxLogMessage(wxT("Invalid network RPT2 value, ignoring")); + if (header->getRptCall2() != m_rptCallsign) { + wxLogMessage("Invalid network RPT2 value, ignoring"); delete header; return; } @@ -417,8 +419,8 @@ void CDStarRepeaterTXThread::processNetworkHeader(CHeaderData* header) unsigned int CDStarRepeaterTXThread::processNetworkFrame(unsigned char* data, unsigned int length, unsigned char seqNo) { - wxASSERT(data != NULL); - wxASSERT(length > 0U); + assert(data != nullptr); + assert(length > 0U); bool end = (seqNo & 0x40U) == 0x40U; if (end) { @@ -499,7 +501,7 @@ void CDStarRepeaterTXThread::endOfNetworkData() if (m_packetCount != 0U) loss = float(m_packetSilence) / float(m_packetCount); - wxLogMessage(wxT("Stats for %s Frames: %.1fs, Loss: %.1f%%, Packets: %u/%u"), m_txHeader->getMyCall1().c_str(), float(m_packetCount) / 50.0F, loss * 100.0F, m_packetSilence, m_packetCount); + wxLogMessage("Stats for %s Frames: %.1fs, Loss: %.1f%%, Packets: %u/%u", m_txHeader->getMyCall1().c_str(), float(m_packetCount) / 50.0F, loss * 100.0F, m_packetSilence, m_packetCount); m_state = DSRS_LISTENING; m_watchdogTimer.stop(); @@ -517,15 +519,15 @@ CDStarRepeaterStatusData* CDStarRepeaterTXThread::getStatus() loss = float(m_packetSilence) / float(m_packetCount); if (m_state == DSRS_LISTENING) - return new CDStarRepeaterStatusData(wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, - wxEmptyString, 0x00, 0x00, 0x00, m_tx, DSRXS_LISTENING, m_state, 0U, 0U, 0U, 0U, 0U, 0U, 0.0F, - wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString); + return new CDStarRepeaterStatusData(std::string(), std::string(), std::string(), std::string(), + std::string(), 0x00, 0x00, 0x00, m_tx, DSRXS_LISTENING, m_state, 0U, 0U, 0U, 0U, 0U, 0U, 0.0F, + std::string(), std::string(), std::string(), std::string(), std::string(), std::string()); else return new CDStarRepeaterStatusData(m_txHeader->getMyCall1(), m_txHeader->getMyCall2(), m_txHeader->getYourCall(), m_txHeader->getRptCall1(), m_txHeader->getRptCall2(), m_txHeader->getFlag1(), m_txHeader->getFlag2(), m_txHeader->getFlag3(), m_tx, DSRXS_LISTENING, - m_state, 0U, 0U, 0U, 0U, 0U, 0U, loss * 100.0F, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, - wxEmptyString, wxEmptyString); + m_state, 0U, 0U, 0U, 0U, 0U, 0U, loss * 100.0F, std::string(), std::string(), std::string(), std::string(), + std::string(), std::string()); } void CDStarRepeaterTXThread::clock(unsigned int ms) @@ -546,26 +548,3 @@ void CDStarRepeaterTXThread::startup() { } -void CDStarRepeaterTXThread::command1() -{ -} - -void CDStarRepeaterTXThread::command2() -{ -} - -void CDStarRepeaterTXThread::command3() -{ -} - -void CDStarRepeaterTXThread::command4() -{ -} - -void CDStarRepeaterTXThread::command5() -{ -} - -void CDStarRepeaterTXThread::command6() -{ -} diff --git a/DStarRepeater/DStarRepeaterTXThread.h b/DStarRepeater/DStarRepeaterTXThread.h index 1716596..21b992b 100644 --- a/DStarRepeater/DStarRepeaterTXThread.h +++ b/DStarRepeater/DStarRepeaterTXThread.h @@ -28,22 +28,33 @@ #include "MQTTPublisher.h" #endif -#include +#include "StdCompat.h" +#include +#include +// CDStarRepeaterTXThread — transmit-only repeater thread (MODE_TXONLY). +// +// Accepts audio streams from the gateway and drives the modem to transmit +// them over RF. There is no radio receive path. The modem's read() is called +// each cycle only to drain any unexpected events; its output is ignored. +// +// Uses the same double-buffered network queue and sequence-number gap-filling +// logic as TRXThread. The state machine is a two-state subset: LISTENING and +// NETWORK. No ack, timeout, beacon, announcement, or control commands. class CDStarRepeaterTXThread : public IDStarRepeaterThread { public: - CDStarRepeaterTXThread(const wxString& type); + CDStarRepeaterTXThread(const std::string& type); virtual ~CDStarRepeaterTXThread(); - virtual void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); + virtual void setCallsign(const std::string& callsign, const std::string& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); virtual void setProtocolHandler(CRepeaterProtocolHandler* handler, bool local); virtual void setModem(CModem* modem); virtual void setController(CExternalController* controller, unsigned int activeHangTime); virtual void setTimes(unsigned int timeout, unsigned int ackTime); - virtual void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language); - virtual void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); + virtual void setBeacon(unsigned int time, const std::string& text, bool voice, TEXT_LANG language); + virtual void setAnnouncement(bool enabled, unsigned int time, const std::string& recordRPT1, const std::string& recordRPT2, const std::string& deleteRPT1, const std::string& deleteRPT2); virtual void setOutputs(bool out1, bool out2, bool out3, bool out4); - virtual void setLogging(bool logging, const wxString& dir); + virtual void setLogging(bool logging, const std::string& dir); virtual void setWhiteList(CCallsignList* list); virtual void setBlackList(CCallsignList* list); virtual void setGreyList(CCallsignList* list); @@ -51,25 +62,17 @@ public: virtual void shutdown(); virtual void startup(); - virtual void command1(); - virtual void command2(); - virtual void command3(); - virtual void command4(); - virtual void command5(); - virtual void command6(); - virtual CDStarRepeaterStatusData* getStatus(); virtual void kill(); - virtual void *Entry(); + virtual void entry(); private: - wxString m_type; + std::string m_type; CModem* m_modem; CRepeaterProtocolHandler* m_protocolHandler; - bool m_stopped; - wxString m_rptCallsign; + std::string m_rptCallsign; CHeaderData* m_txHeader; COutputQueue** m_networkQueue; unsigned int m_writeNum; @@ -81,11 +84,11 @@ private: DSTAR_RPT_STATE m_state; bool m_tx; unsigned int m_space; - bool m_killed; + std::atomic m_killed; unsigned char* m_lastData; CAMBEFEC m_ambe; - wxStopWatch m_headerTime; - wxStopWatch m_packetTime; + std::chrono::steady_clock::time_point m_headerTime; + std::chrono::steady_clock::time_point m_packetTime; unsigned int m_packetCount; unsigned int m_packetSilence; diff --git a/DStarRepeater/DStarRepeaterThread.cpp b/DStarRepeater/DStarRepeaterThread.cpp index b698094..2a8c343 100644 --- a/DStarRepeater/DStarRepeaterThread.cpp +++ b/DStarRepeater/DStarRepeaterThread.cpp @@ -13,8 +13,7 @@ #include "DStarRepeaterThread.h" -IDStarRepeaterThread::IDStarRepeaterThread(): -wxThread(wxTHREAD_JOINABLE) +IDStarRepeaterThread::IDStarRepeaterThread() { } diff --git a/DStarRepeater/DStarRepeaterThread.h b/DStarRepeater/DStarRepeaterThread.h index 5df4b93..49cdf0e 100644 --- a/DStarRepeater/DStarRepeaterThread.h +++ b/DStarRepeater/DStarRepeaterThread.h @@ -16,6 +16,26 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* + * IDStarRepeaterThread is the abstract interface implemented by all repeater + * thread variants (TRX, TX-only, RX-only, TXRX split). + * + * Lifecycle + * --------- + * main() (createThread()) selects the concrete subclass, + * calls the set* configuration methods, then launches the thread by calling + * entry() inside a std::thread stored in the public m_thread member. The + * thread runs until kill() is called, after which main() joins m_thread. + * + * Thread ownership + * ---------------- + * The set* methods are called from the main thread before the worker thread + * starts, so no locking is needed there. shutdown() and startup() may be + * called from the main thread at runtime (e.g. in response to a control + * command) and must be safe to call concurrently with entry(). getStatus() + * is likewise called from the main thread to refresh the GUI. + */ + #ifndef DStarRepeaterThread_H #define DStarRepeaterThread_H @@ -26,21 +46,24 @@ #include "CallsignList.h" #include "Modem.h" -#include +#include "StdCompat.h" +#include +// Classification of a received DV frame, used to decide how to route it. enum FRAME_TYPE { - FRAME_NORMAL, - FRAME_SYNC, - FRAME_END + FRAME_NORMAL, // Regular DV payload frame + FRAME_SYNC, // Frame containing a DATA_SYNC marker + FRAME_END // End-of-transmission frame }; -class IDStarRepeaterThread : public wxThread { +class IDStarRepeaterThread { public: IDStarRepeaterThread(); virtual ~IDStarRepeaterThread() = 0; - virtual void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply) = 0; + // Called from main() before the thread starts to configure the repeater identity. + virtual void setCallsign(const std::string& callsign, const std::string& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply) = 0; virtual void setProtocolHandler(CRepeaterProtocolHandler* handler, bool local) = 0; virtual void setModem(CModem* modem) = 0; @@ -48,27 +71,38 @@ public: virtual void setTimes(unsigned int timeout, unsigned int ackTime) = 0; - virtual void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language) = 0; - virtual void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2) = 0; - virtual void setControl(bool enabled, const wxString& rpt1Callsign, - const wxString& rpt2Callsign, const wxString& shutdown, - const wxString& startup, const wxArrayString &command, - const wxArrayString& status, const wxArrayString& outputs + virtual void setBeacon(unsigned int time, const std::string& text, bool voice, TEXT_LANG language) = 0; + virtual void setAnnouncement(bool enabled, unsigned int time, const std::string& recordRPT1, const std::string& recordRPT2, const std::string& deleteRPT1, const std::string& deleteRPT2) = 0; + // setControl has a default no-op so TX/RX-only threads need not implement it. + virtual void setControl(bool enabled, const std::string& rpt1Callsign, + const std::string& rpt2Callsign, const std::string& shutdown, + const std::string& startup, const std::vector& command, + const std::vector& commandLine, + const std::vector& status, const std::vector& outputs ) { }; virtual void setOutputs(bool out1, bool out2, bool out3, bool out4) = 0; - virtual void setLogging(bool logging, const wxString& dir) = 0; + virtual void setLogging(bool logging, const std::string& dir) = 0; virtual void setWhiteList(CCallsignList* list) = 0; virtual void setBlackList(CCallsignList* list) = 0; virtual void setGreyList(CCallsignList* list) = 0; + // Administratively shut down or restart the repeater without stopping the thread. virtual void shutdown() = 0; virtual void startup() = 0; + // Called from the main thread to poll repeater state for GUI or MQTT reporting. virtual CDStarRepeaterStatusData* getStatus() = 0; + // The thread body: runs the repeater state machine until kill() is called. + virtual void entry() = 0; + + // Signal the thread to exit its main loop; join m_thread after calling this. virtual void kill() = 0; + // The worker thread, launched by createThread() in the application. + std::thread m_thread; + private: }; diff --git a/DStarRepeater/Makefile b/DStarRepeater/Makefile index a457108..8e8255a 100644 --- a/DStarRepeater/Makefile +++ b/DStarRepeater/Makefile @@ -1,4 +1,4 @@ -OBJECTS = DStarRepeaterApp.o DStarRepeaterLogRedirect.o DStarRepeaterRXThread.o DStarRepeaterStatusData.o \ +OBJECTS = DStarRepeaterApp.o DStarRepeaterRXThread.o DStarRepeaterStatusData.o \ DStarRepeaterThread.o DStarRepeaterTRXThread.o DStarRepeaterTXRXThread.o DStarRepeaterTXThread.o .PHONY: all install clean @@ -6,13 +6,13 @@ OBJECTS = DStarRepeaterApp.o DStarRepeaterLogRedirect.o DStarRepeaterRXThread.o all: dstarrepeaterd dstarrepeaterd: $(OBJECTS) ../Common/Common.a - $(CXX) $(OBJECTS) ../Common/Common.a $(LDFLAGS) $(LIBS) -o dstarrepeaterd + $(CXX) $(OBJECTS) ../Common/Common.a $(LDFLAGS) $(LIBS) -ldl -o dstarrepeaterd -include $(OBJECTS:.o=.d) %.o: %.cpp - $(CXX) -DwxUSE_GUI=0 $(CFLAGS) -I../Common -c -o $@ $< - $(CXX) -MM -DwxUSE_GUI=0 $(CFLAGS) -I../Common $< > $*.d + $(CXX) $(CFLAGS) -I../Common -c -o $@ $< + $(CXX) -MM $(CFLAGS) -I../Common $< > $*.d install: install -g root -o root -m 0755 dstarrepeaterd $(DESTDIR)$(BINDIR) diff --git a/DStarRepeater/MakefileGUI b/DStarRepeater/MakefileGUI deleted file mode 100644 index 50494b7..0000000 --- a/DStarRepeater/MakefileGUI +++ /dev/null @@ -1,23 +0,0 @@ -OBJECTS = DStarRepeaterApp.o DStarRepeaterFrame.o DStarRepeaterLogRedirect.o DStarRepeaterRXThread.o DStarRepeaterStatusData.o \ - DStarRepeaterThread.o DStarRepeaterTRXThread.o DStarRepeaterTXRXThread.o DStarRepeaterTXThread.o - -.PHONY: install clean - -all: dstarrepeater - -dstarrepeater: $(OBJECTS) ../Common/Common.a ../GUICommon/GUICommon.a - $(CXX) $(OBJECTS) ../Common/Common.a ../GUICommon/GUICommon.a $(LDFLAGS) $(GUILIBS) -o dstarrepeater - -%.o: %.cpp - $(CXX) $(CFLAGS) -I../Common -I../GUICommon -c -o $@ $< - $(CXX) -MM $(CFLAGS) -I../Common -I../GUICommon $< > $*.d - -install: - install -g root -o root -m 0755 dstarrepeater $(DESTDIR)$(BINDIR) - -clean: - $(RM) dstarrepeater *.o *.d *.bak *~ - -../Common/Common.a: -../GUICommon/GUICommon.a: - diff --git a/DStarRepeater32.nsi b/DStarRepeater32.nsi deleted file mode 100644 index 6c59724..0000000 --- a/DStarRepeater32.nsi +++ /dev/null @@ -1,108 +0,0 @@ -;NSIS Modern User Interface -;Repeater install script -;Written by Jonathan Naylor - -;-------------------------------- -;Include Modern UI - - !include "MUI2.nsh" - -;-------------------------------- -;Configuration - - ;General - Name "DStarRepeater 20180911" - OutFile "DStarRepeater32-20180911.exe" - - ;Folder selection page - InstallDir "$PROGRAMFILES\D-Star Repeater" - - ;Request application privileges for Windows Vista - RequestExecutionLevel admin - -;-------------------------------- -;Interface Settings - - !define MUI_ABORTWARNING - -;-------------------------------- -;Pages - - !insertmacro MUI_PAGE_LICENSE "COPYING.txt" - !insertmacro MUI_PAGE_DIRECTORY - !insertmacro MUI_PAGE_INSTFILES - !insertmacro MUI_UNPAGE_CONFIRM - !insertmacro MUI_UNPAGE_INSTFILES - -;-------------------------------- -;Languages - - !insertmacro MUI_LANGUAGE "English" - -;-------------------------------- -;Installer Sections - -Section "Repeater Program Files" SecProgram - - SetOutPath "$INSTDIR" - - File "Release\DStarRepeater.exe" - File "Release\DStarRepeaterConfig.exe" - File "..\portaudio\build\msvc\Win32\Release\portaudio_x86.dll" - File "C:\wxWidgets-3.0.4\lib\vc_dll\wxbase30u_vc_custom.dll" - File "C:\wxWidgets-3.0.4\lib\vc_dll\wxmsw30u_adv_vc_custom.dll" - File "C:\wxWidgets-3.0.4\lib\vc_dll\wxmsw30u_core_vc_custom.dll" - File "WindowsUSB\xDVRPTR-32-64-2.inf" - File "WindowsUSB\dvrptr_cdc.inf" - File "WindowsUSB\gmsk.inf" - File "WindowsUSB\gmsk.cat" - File "Data\de_DE.ambe" - File "Data\de_DE.indx" - File "Data\dk_DK.ambe" - File "Data\dk_DK.indx" - File "Data\en_GB.ambe" - File "Data\en_GB.indx" - File "Data\en_US.ambe" - File "Data\en_US.indx" - File "Data\es_ES.ambe" - File "Data\es_ES.indx" - File "Data\fr_FR.ambe" - File "Data\fr_FR.indx" - File "Data\it_IT.ambe" - File "Data\it_IT.indx" - File "Data\no_NO.ambe" - File "Data\no_NO.indx" - File "Data\pl_PL.ambe" - File "Data\pl_PL.indx" - File "Data\se_SE.ambe" - File "Data\se_SE.indx" - File "COPYING.txt" - File "CHANGES.txt" - - ;Create start menu entry - CreateDirectory "$SMPROGRAMS\D-Star Repeater" - CreateShortCut "$SMPROGRAMS\D-Star Repeater\D-Star Repeater.lnk" "$INSTDIR\DStarRepeater.exe" - CreateShortCut "$SMPROGRAMS\D-Star Repeater\D-Star Repeater Config.lnk" "$INSTDIR\DStarRepeaterConfig.exe" - CreateShortCut "$SMPROGRAMS\D-Star Repeater\Licence.lnk" "$INSTDIR\COPYING.txt" - CreateShortCut "$SMPROGRAMS\D-Star Repeater\Changes.lnk" "$INSTDIR\CHANGES.txt" - CreateShortCut "$SMPROGRAMS\D-Star Repeater\Uninstall.lnk" "$INSTDIR\Uninstall.exe" - - ;Create uninstaller - WriteUninstaller "$INSTDIR\Uninstall.exe" - -SectionEnd - -;-------------------------------- -;Uninstaller Section - -Section "Uninstall" - - Delete "$INSTDIR\*.*" - RMDir "$INSTDIR" - - Delete "$SMPROGRAMS\D-Star Repeater\*.*" - RMDir "$SMPROGRAMS\D-Star Repeater" - - DeleteRegKey /ifempty HKCU "Software\G4KLX\D-Star Repeater" - -SectionEnd diff --git a/DStarRepeater64.nsi b/DStarRepeater64.nsi deleted file mode 100644 index f8f5cf4..0000000 --- a/DStarRepeater64.nsi +++ /dev/null @@ -1,108 +0,0 @@ -;NSIS Modern User Interface -;Repeater install script -;Written by Jonathan Naylor - -;-------------------------------- -;Include Modern UI - - !include "MUI2.nsh" - -;-------------------------------- -;Configuration - - ;General - Name "DStarRepeater 20180911" - OutFile "DStarRepeater64-20180911.exe" - - ;Folder selection page - InstallDir "$PROGRAMFILES64\D-Star Repeater" - - ;Request application privileges for Windows Vista - RequestExecutionLevel admin - -;-------------------------------- -;Interface Settings - - !define MUI_ABORTWARNING - -;-------------------------------- -;Pages - - !insertmacro MUI_PAGE_LICENSE "COPYING.txt" - !insertmacro MUI_PAGE_DIRECTORY - !insertmacro MUI_PAGE_INSTFILES - !insertmacro MUI_UNPAGE_CONFIRM - !insertmacro MUI_UNPAGE_INSTFILES - -;-------------------------------- -;Languages - - !insertmacro MUI_LANGUAGE "English" - -;-------------------------------- -;Installer Sections - -Section "Repeater Program Files" SecProgram - - SetOutPath "$INSTDIR" - - File "x64\Release\DStarRepeater.exe" - File "x64\Release\DStarRepeaterConfig.exe" - File "..\portaudio\build\msvc\x64\Release\portaudio_x64.dll" - File "C:\wxWidgets-3.0.4\lib\vc_x64_dll\wxbase30u_vc_x64_custom.dll" - File "C:\wxWidgets-3.0.4\lib\vc_x64_dll\wxmsw30u_adv_vc_x64_custom.dll" - File "C:\wxWidgets-3.0.4\lib\vc_x64_dll\wxmsw30u_core_vc_x64_custom.dll" - File "WindowsUSB\xDVRPTR-32-64-2.inf" - File "WindowsUSB\dvrptr_cdc.inf" - File "WindowsUSB\gmsk.inf" - File "WindowsUSB\gmsk.cat" - File "Data\de_DE.ambe" - File "Data\de_DE.indx" - File "Data\dk_DK.ambe" - File "Data\dk_DK.indx" - File "Data\en_GB.ambe" - File "Data\en_GB.indx" - File "Data\en_US.ambe" - File "Data\en_US.indx" - File "Data\es_ES.ambe" - File "Data\es_ES.indx" - File "Data\fr_FR.ambe" - File "Data\fr_FR.indx" - File "Data\it_IT.ambe" - File "Data\it_IT.indx" - File "Data\no_NO.ambe" - File "Data\no_NO.indx" - File "Data\pl_PL.ambe" - File "Data\pl_PL.indx" - File "Data\se_SE.ambe" - File "Data\se_SE.indx" - File "COPYING.txt" - File "CHANGES.txt" - - ;Create start menu entry - CreateDirectory "$SMPROGRAMS\D-Star Repeater" - CreateShortCut "$SMPROGRAMS\D-Star Repeater\D-Star Repeater.lnk" "$INSTDIR\DStarRepeater.exe" - CreateShortCut "$SMPROGRAMS\D-Star Repeater\D-Star Repeater Config.lnk" "$INSTDIR\DStarRepeaterConfig.exe" - CreateShortCut "$SMPROGRAMS\D-Star Repeater\Licence.lnk" "$INSTDIR\COPYING.txt" - CreateShortCut "$SMPROGRAMS\D-Star Repeater\Changes.lnk" "$INSTDIR\CHANGES.txt" - CreateShortCut "$SMPROGRAMS\D-Star Repeater\Uninstall.lnk" "$INSTDIR\Uninstall.exe" - - ;Create uninstaller - WriteUninstaller "$INSTDIR\Uninstall.exe" - -SectionEnd - -;-------------------------------- -;Uninstaller Section - -Section "Uninstall" - - Delete "$INSTDIR\*.*" - RMDir "$INSTDIR" - - Delete "$SMPROGRAMS\D-Star Repeater\*.*" - RMDir "$SMPROGRAMS\D-Star Repeater" - - DeleteRegKey /ifempty HKCU "Software\G4KLX\D-Star Repeater" - -SectionEnd diff --git a/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj b/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj deleted file mode 100644 index 8ac5c7d..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj +++ /dev/null @@ -1,230 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {FBAE8201-11CC-4256-BF39-A860B9DF963C} - DStarRepeaterConfig - Win32Proj - 10.0 - - - - Application - v142 - Unicode - true - - - Application - v142 - Unicode - true - - - Application - v142 - Unicode - - - Application - v142 - Unicode - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>14.0.24720.0 - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - true - - - true - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - false - - - false - - - - Disabled - $(WXWIN)\include\msvc;$(SolutionDir)..\portaudio\include;$(SolutionDir)Common;$(SolutionDir)GUICommon;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - - Level4 - EditAndContinue - - - portaudio_x86.lib;wsock32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) - $(SolutionDir)..\portaudio\build\msvc\Win32\Debug;$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) - true - Windows - MachineX86 - - - - - Disabled - $(WXWIN)\include\msvc;$(SolutionDir)..\portaudio\include;$(SolutionDir)Common;$(SolutionDir)GUICommon;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebugDLL - - - Level4 - ProgramDatabase - - - portaudio_x64.lib;wsock32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) - $(SolutionDir)..\portaudio\build\msvc\x64\Debug;$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) - true - Windows - - - - - MaxSpeed - true - $(WXWIN)\include\msvc;$(SolutionDir)..\portaudio\include;$(SolutionDir)Common;$(SolutionDir)GUICommon;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - Level3 - ProgramDatabase - - - portaudio_x86.lib;wsock32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) - $(SolutionDir)..\portaudio\build\msvc\Win32\Release;$(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) - true - Windows - true - true - MachineX86 - - - - - MaxSpeed - true - $(WXWIN)\include\msvc;$(SolutionDir)..\portaudio\include;$(SolutionDir)Common;$(SolutionDir)GUICommon;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - - - portaudio_x64.lib;wsock32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) - $(SolutionDir)..\portaudio\build\msvc\x64\Release;$(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) - true - Windows - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {3753ef20-2330-415e-b933-2560a474498b} - false - - - {2c548aab-a9f7-496f-bcf5-6185bda304d2} - false - - - - - - \ No newline at end of file diff --git a/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj.filters b/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj.filters deleted file mode 100644 index 5864889..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfig.vcxproj.filters +++ /dev/null @@ -1,152 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.cpp deleted file mode 100644 index 34dff7d..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigActiveHangSet.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2010,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigActiveHangSet.h" - -const unsigned int TIMED_WIDTH = 300U; - -const unsigned int BORDER_SIZE = 5U; - -CDStarRepeaterConfigActiveHangSet::CDStarRepeaterConfigActiveHangSet(wxWindow* parent, int id, const wxString& title, unsigned int time) : -wxPanel(parent, id), -m_title(title), -m_time(NULL) -{ - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* timeLabel = new wxStaticText(this, -1, _("Time (secs)")); - sizer->Add(timeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_time = new wxSlider(this, -1, time, 0, 300, wxDefaultPosition, wxSize(TIMED_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_time, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - SetAutoLayout(true); - - sizer->Fit(this); - sizer->SetSizeHints(this); - - SetSizer(sizer); -} - - -CDStarRepeaterConfigActiveHangSet::~CDStarRepeaterConfigActiveHangSet() -{ -} - -bool CDStarRepeaterConfigActiveHangSet::Validate() -{ - return true; -} - -unsigned int CDStarRepeaterConfigActiveHangSet::getTime() const -{ - return m_time->GetValue(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.cpp deleted file mode 100644 index a594ad2..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2013,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigAnnouncementSet.h" - -const unsigned int CONTROL_SIZE = 100U; -const unsigned int TIMES_WIDTH = 300U; -const unsigned int BORDER_SIZE = 5U; - -CDStarRepeaterConfigAnnouncementSet::CDStarRepeaterConfigAnnouncementSet(wxWindow* parent, int id, const wxString& title, bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2) : -wxPanel(parent, id), -m_title(title), -m_enabled(NULL), -m_time(NULL), -m_recordRPT1(NULL), -m_recordRPT2(NULL), -m_deleteRPT1(NULL), -m_deleteRPT2(NULL) -{ - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* enabledText = new wxStaticText(this, -1, _("Enabled")); - sizer->Add(enabledText, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_enabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_SIZE, -1)); - m_enabled->Append(_("Disabled")); - m_enabled->Append(_("Enabled")); - sizer->Add(m_enabled, 0, wxALL, BORDER_SIZE); - m_enabled->SetSelection(enabled ? 1 : 0); - - wxStaticText* timeLabel = new wxStaticText(this, -1, _("Time (mins)")); - sizer->Add(timeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_time = new wxSlider(this, -1, time / 60U, 0, 30, wxDefaultPosition, wxSize(TIMES_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_time, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* recordRPT1Text = new wxStaticText(this, -1, _("Record RPT1")); - sizer->Add(recordRPT1Text, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_recordRPT1 = new CCallsignTextCtrl(this, -1, recordRPT1, wxDefaultPosition, wxSize(CONTROL_SIZE, -1)); - m_recordRPT1->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_recordRPT1, 0, wxALL, BORDER_SIZE); - - wxStaticText* recordRPT2Text = new wxStaticText(this, -1, _("Record RPT2")); - sizer->Add(recordRPT2Text, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_recordRPT2 = new CCallsignTextCtrl(this, -1, recordRPT2, wxDefaultPosition, wxSize(CONTROL_SIZE, -1)); - m_recordRPT2->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_recordRPT2, 0, wxALL, BORDER_SIZE); - - wxStaticText* deleteRPT1Text = new wxStaticText(this, -1, _("Delete RPT1")); - sizer->Add(deleteRPT1Text, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_deleteRPT1 = new CCallsignTextCtrl(this, -1, deleteRPT1, wxDefaultPosition, wxSize(CONTROL_SIZE, -1)); - m_deleteRPT1->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_deleteRPT1, 0, wxALL, BORDER_SIZE); - - wxStaticText* deleteRPT2Text = new wxStaticText(this, -1, _("Delete RPT2")); - sizer->Add(deleteRPT2Text, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_deleteRPT2 = new CCallsignTextCtrl(this, -1, deleteRPT2, wxDefaultPosition, wxSize(CONTROL_SIZE, -1)); - m_deleteRPT2->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_deleteRPT2, 0, wxALL, BORDER_SIZE); - - SetAutoLayout(true); - - sizer->Fit(this); - sizer->SetSizeHints(this); - - SetSizer(sizer); -} - - -CDStarRepeaterConfigAnnouncementSet::~CDStarRepeaterConfigAnnouncementSet() -{ -} - -bool CDStarRepeaterConfigAnnouncementSet::Validate() -{ - return m_enabled->GetCurrentSelection() != wxNOT_FOUND; -} - -bool CDStarRepeaterConfigAnnouncementSet::getEnabled() const -{ - int n = m_enabled->GetCurrentSelection(); - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -unsigned int CDStarRepeaterConfigAnnouncementSet::getTime() const -{ - return m_time->GetValue() * 60U; -} - -wxString CDStarRepeaterConfigAnnouncementSet::getRecordRPT1() const -{ - return m_recordRPT1->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigAnnouncementSet::getRecordRPT2() const -{ - return m_recordRPT2->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigAnnouncementSet::getDeleteRPT1() const -{ - return m_deleteRPT1->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigAnnouncementSet::getDeleteRPT2() const -{ - return m_deleteRPT2->GetValue().MakeUpper(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.h b/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.h deleted file mode 100644 index bc6bca8..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigAnnouncementSet.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2013,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigAnnouncementSet_H -#define DStarRepeaterConfigAnnouncementSet_H - -#include "CallsignTextCtrl.h" -#include "DStarDefines.h" - -#include - -class CDStarRepeaterConfigAnnouncementSet : public wxPanel { -public: - CDStarRepeaterConfigAnnouncementSet(wxWindow* parent, int id, const wxString& title, bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2); - virtual ~CDStarRepeaterConfigAnnouncementSet(); - - virtual bool Validate(); - - virtual bool getEnabled() const; - virtual unsigned int getTime() const; - virtual wxString getRecordRPT1() const; - virtual wxString getRecordRPT2() const; - virtual wxString getDeleteRPT1() const; - virtual wxString getDeleteRPT2() const; - -private: - wxString m_title; - wxChoice* m_enabled; - wxSlider* m_time; - CCallsignTextCtrl* m_recordRPT1; - CCallsignTextCtrl* m_recordRPT2; - CCallsignTextCtrl* m_deleteRPT1; - CCallsignTextCtrl* m_deleteRPT2; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigApp.cpp b/DStarRepeaterConfig/DStarRepeaterConfigApp.cpp deleted file mode 100644 index 10a01c7..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigApp.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2011-2014,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 "DStarRepeaterConfigApp.h" -#include "DStarDefines.h" -#include "Version.h" - -#include -#include - -wxIMPLEMENT_APP(CDStarRepeaterConfigApp); - -const wxChar* NAME_PARAM = wxT("Repeater Name"); -const wxChar* CONFDIR_OPTION = wxT("confdir"); - - -CDStarRepeaterConfigApp::CDStarRepeaterConfigApp() : -wxApp(), -m_name(), -m_confDir(), -m_frame(NULL) -{ -} - -CDStarRepeaterConfigApp::~CDStarRepeaterConfigApp() -{ -} - -bool CDStarRepeaterConfigApp::OnInit() -{ - SetVendorName(VENDOR_NAME); - - if (!wxApp::OnInit()) - return false; - - wxString frameName = APPLICATION_NAME + wxT(" - "); - if (!m_name.IsEmpty()) { - frameName.Append(m_name); - frameName.Append(wxT(" - ")); - } - frameName.Append(VERSION); - - m_frame = new CDStarRepeaterConfigFrame(frameName, m_confDir, m_name); - m_frame->Show(); - - SetTopWindow(m_frame); - - return true; -} - -int CDStarRepeaterConfigApp::OnExit() -{ - return 0; -} - -void CDStarRepeaterConfigApp::OnInitCmdLine(wxCmdLineParser& parser) -{ - parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); - parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); - - wxApp::OnInitCmdLine(parser); -} - -bool CDStarRepeaterConfigApp::OnCmdLineParsed(wxCmdLineParser& parser) -{ - if (!wxApp::OnCmdLineParsed(parser)) - return false; - - wxString confDir; - bool found = parser.Found(CONFDIR_OPTION, &confDir); - if (found) - m_confDir = confDir; - - if (parser.GetParamCount() > 0U) - m_name = parser.GetParam(0U); - - return true; -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigApp.h b/DStarRepeaterConfig/DStarRepeaterConfigApp.h deleted file mode 100644 index 0fac17e..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigApp.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2011,2012,2013,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 DStarRepeaterConfigApp_H -#define DStarRepeaterConfigApp_H - -#include "DStarRepeaterConfigFrame.h" -#include "DStarRepeaterConfigDefs.h" - -#include - -class CDStarRepeaterConfigApp : public wxApp { - -public: - CDStarRepeaterConfigApp(); - virtual ~CDStarRepeaterConfigApp(); - - virtual bool OnInit(); - virtual int OnExit(); - - virtual void OnInitCmdLine(wxCmdLineParser& parser); - virtual bool OnCmdLineParsed(wxCmdLineParser& parser); - -private: - wxString m_name; - wxString m_confDir; - CDStarRepeaterConfigFrame* m_frame; -}; - -wxDECLARE_APP(CDStarRepeaterConfigApp); - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.cpp deleted file mode 100644 index 053c72b..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2012,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigBeaconSet.h" - -const unsigned int MAX_MESSAGE_LENGTH = 20U; - -const unsigned int CONTROL_SIZE1 = 200U; -const unsigned int CONTROL_SIZE2 = 100U; -const unsigned int TIMES_WIDTH = 300U; -const unsigned int BORDER_SIZE = 5U; - -CDStarRepeaterConfigBeaconSet::CDStarRepeaterConfigBeaconSet(wxWindow* parent, int id, const wxString& title, unsigned int time, const wxString& text, bool voice, TEXT_LANG language) : -wxPanel(parent, id), -m_title(title), -m_time(NULL), -m_text(NULL), -m_voice(NULL), -m_language(NULL) -{ - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* timeLabel = new wxStaticText(this, -1, _("Time (mins)")); - sizer->Add(timeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_time = new wxSlider(this, -1, time / 60U, 0, 30, wxDefaultPosition, wxSize(TIMES_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_time, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* messageText = new wxStaticText(this, -1, _("Message")); - sizer->Add(messageText, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_text = new CMessageTextCtrl(this, -1, text, wxDefaultPosition, wxSize(CONTROL_SIZE1, -1)); - m_text->SetMaxLength(MAX_MESSAGE_LENGTH); - sizer->Add(m_text, 0, wxALL, BORDER_SIZE); - - wxStaticText* voiceText = new wxStaticText(this, -1, _("Voice")); - sizer->Add(voiceText, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_voice = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_SIZE2, -1)); - m_voice->Append(_("Disabled")); - m_voice->Append(_("Enabled")); - sizer->Add(m_voice, 0, wxALL, BORDER_SIZE); - m_voice->SetSelection(voice ? 1 : 0); - - wxStaticText* languageLabel = new wxStaticText(this, -1, _("Language")); - sizer->Add(languageLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_language = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_SIZE2, -1)); - m_language->Append(wxT("English (UK)")); - m_language->Append(wxT("Deutsch")); - m_language->Append(wxT("Dansk")); - m_language->Append(wxT("Francais")); - m_language->Append(wxT("Italiano")); - m_language->Append(wxT("Polski")); - m_language->Append(wxT("Espanol")); - m_language->Append(wxT("Svenska")); - m_language->Append(wxT("Nederlands")); - m_language->Append(wxT("English (US)")); - m_language->Append(wxT("Norsk")); - sizer->Add(m_language, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_language->SetSelection(int(language)); - - SetAutoLayout(true); - - sizer->Fit(this); - sizer->SetSizeHints(this); - - SetSizer(sizer); -} - - -CDStarRepeaterConfigBeaconSet::~CDStarRepeaterConfigBeaconSet() -{ -} - -bool CDStarRepeaterConfigBeaconSet::Validate() -{ - if (m_voice->GetCurrentSelection() == wxNOT_FOUND) - return false; - - return m_language->GetCurrentSelection() != wxNOT_FOUND; -} - -unsigned int CDStarRepeaterConfigBeaconSet::getTime() const -{ - return m_time->GetValue() * 60U; -} - -wxString CDStarRepeaterConfigBeaconSet::getText() const -{ - return m_text->GetValue(); -} - -bool CDStarRepeaterConfigBeaconSet::getVoice() const -{ - int n = m_voice->GetSelection(); - - return n == 1; -} - -TEXT_LANG CDStarRepeaterConfigBeaconSet::getLanguage() const -{ - int n = m_language->GetCurrentSelection(); - if (n == wxNOT_FOUND) - return TL_ENGLISH_UK; - - return TEXT_LANG(n); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.h b/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.h deleted file mode 100644 index b9a0256..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigBeaconSet.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2012,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigBeaconSet_H -#define DStarRepeaterConfigBeaconSet_H - -#include "MessageTextCtrl.h" -#include "DStarDefines.h" - -#include - -class CDStarRepeaterConfigBeaconSet : public wxPanel { -public: - CDStarRepeaterConfigBeaconSet(wxWindow* parent, int id, const wxString& title, unsigned int time, const wxString& text, bool voice, TEXT_LANG language); - virtual ~CDStarRepeaterConfigBeaconSet(); - - virtual bool Validate(); - - virtual unsigned int getTime() const; - virtual wxString getText() const; - virtual bool getVoice() const; - virtual TEXT_LANG getLanguage() const; - -private: - wxString m_title; - wxSlider* m_time; - CMessageTextCtrl* m_text; - wxChoice* m_voice; - wxChoice* m_language; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.cpp deleted file mode 100644 index 609277b..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigCallsignSet.h" - -const unsigned int CALLSIGN_WIDTH = 80U; -const unsigned int SUFFIX_WIDTH = 50U; - -const unsigned int BORDER_SIZE = 5U; - -const int CHOICE_MODE = 8745; - -BEGIN_EVENT_TABLE(CDStarRepeaterConfigCallsignSet, wxPanel) - EVT_CHOICE(CHOICE_MODE, CDStarRepeaterConfigCallsignSet::onMode) -END_EVENT_TABLE() - - -CDStarRepeaterConfigCallsignSet::CDStarRepeaterConfigCallsignSet(wxWindow* parent, int id, const wxString& title, const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply) : -wxPanel(parent, id), -m_title(title), -m_callsign(NULL), -m_gateway(NULL), -m_suffix(NULL), -m_mode(NULL), -m_ack(NULL), -m_restriction(NULL), -m_rpt1Validation(NULL), -m_dtmfBlanking(NULL), -m_errorReply(NULL) -{ - wxFlexGridSizer* sizer = new wxFlexGridSizer(3); - - wxStaticText* callsignLabel = new wxStaticText(this, -1, _("Callsign")); - sizer->Add(callsignLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - wxString call = callsign; - call.Append(wxT(" ")); - call.Truncate(LONG_CALLSIGN_LENGTH); - - m_callsign = new CCallsignTextCtrl(this, -1, call.Left(LONG_CALLSIGN_LENGTH - 1U).Trim(), wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); - m_callsign->SetMaxLength(LONG_CALLSIGN_LENGTH - 1U); - sizer->Add(m_callsign, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_suffix = new wxChoice(this, -1, wxDefaultPosition, wxSize(SUFFIX_WIDTH, -1)); -#if defined(__WXDEBUG__) - m_suffix->Append(wxT(" ")); -#endif - m_suffix->Append(wxT("A")); - m_suffix->Append(wxT("B")); - m_suffix->Append(wxT("C")); - m_suffix->Append(wxT("D")); - m_suffix->Append(wxT("E")); - sizer->Add(m_suffix, 0, wxALL, BORDER_SIZE); - - wxString suffix = callsign.Right(1U); - bool res = m_suffix->SetStringSelection(suffix); - if (!res) - m_suffix->SetStringSelection(wxT("B")); - - wxStaticText* gatewayLabel = new wxStaticText(this, -1, _("Gateway")); - sizer->Add(gatewayLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - call = gateway; - call.Append(wxT(" ")); - call.Truncate(LONG_CALLSIGN_LENGTH); - - m_gateway = new CCallsignTextCtrl(this, -1, call.Left(LONG_CALLSIGN_LENGTH - 1U).Trim(), wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); - m_gateway->SetMaxLength(LONG_CALLSIGN_LENGTH - 1U); - sizer->Add(m_gateway, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* gLabel = new wxStaticText(this, -1, wxT("G")); - sizer->Add(gLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* modeLabel = new wxStaticText(this, -1, _("Mode")); - sizer->Add(modeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_mode = new wxChoice(this, CHOICE_MODE, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); - m_mode->Append(_("Duplex")); - m_mode->Append(_("Simplex")); - m_mode->Append(_("Gateway")); - m_mode->Append(_("TX Only")); - m_mode->Append(_("RX Only")); - m_mode->Append(_("TX and RX")); - sizer->Add(m_mode, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_mode->SetSelection(int(mode)); - - wxStaticText* dummy1 = new wxStaticText(this, -1, wxEmptyString); - sizer->Add(dummy1, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* ackLabel = new wxStaticText(this, -1, _("Ack")); - sizer->Add(ackLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_ack = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); - m_ack->Append(_("Off")); - m_ack->Append(wxT("BER")); - m_ack->Append(_("Status")); - sizer->Add(m_ack, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_ack->SetSelection(int(ack)); - - wxStaticText* dummy2 = new wxStaticText(this, -1, wxEmptyString); - sizer->Add(dummy2, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* restrictionLabel = new wxStaticText(this, -1, _("Restrict")); - sizer->Add(restrictionLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_restriction = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); - m_restriction->Append(_("Off")); - m_restriction->Append(_("On")); - sizer->Add(m_restriction, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_restriction->SetSelection(restriction ? 1 : 0); - - wxStaticText* dummy3 = new wxStaticText(this, -1, wxEmptyString); - sizer->Add(dummy3, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* rpt1ValidationLabel = new wxStaticText(this, -1, _("RPT1 Validation")); - sizer->Add(rpt1ValidationLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_rpt1Validation = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); - m_rpt1Validation->Append(_("Off")); - m_rpt1Validation->Append(_("On")); - sizer->Add(m_rpt1Validation, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_rpt1Validation->SetSelection(rpt1Validation ? 1 : 0); - - wxStaticText* dummy4 = new wxStaticText(this, -1, wxEmptyString); - sizer->Add(dummy4, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* dtmfBlankingLabel = new wxStaticText(this, -1, _("DTMF Blanking")); - sizer->Add(dtmfBlankingLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_dtmfBlanking = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); - m_dtmfBlanking->Append(_("Off")); - m_dtmfBlanking->Append(_("On")); - sizer->Add(m_dtmfBlanking, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_dtmfBlanking->SetSelection(dtmfBlanking ? 1 : 0); - - wxStaticText* dummy5 = new wxStaticText(this, -1, wxEmptyString); - sizer->Add(dummy5, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* errorReplyLabel = new wxStaticText(this, -1, _("Error Reply")); - sizer->Add(errorReplyLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_errorReply = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); - m_errorReply->Append(_("Off")); - m_errorReply->Append(_("On")); - sizer->Add(m_errorReply, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_errorReply->SetSelection(errorReply ? 1 : 0); - - SetAutoLayout(true); - - sizer->Fit(this); - sizer->SetSizeHints(this); - - SetSizer(sizer); -} - - -CDStarRepeaterConfigCallsignSet::~CDStarRepeaterConfigCallsignSet() -{ -} - -bool CDStarRepeaterConfigCallsignSet::Validate() -{ - bool res = getCallsign().IsEmpty(); - if (res) { - wxMessageDialog dialog(this, _("The Callsign may not be empty"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - if (getCallsign().Left(2U).IsSameAs(wxT("F0"))) { - wxMessageDialog dialog(this, _("The Callsign may not be for a French novice"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - res = getGateway().IsEmpty(); - if (res) { - wxMessageDialog dialog(this, _("The Gateway may not be empty"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - if (m_suffix->GetSelection() == wxNOT_FOUND) { - wxMessageDialog dialog(this, _("The Callsign suffix is not set"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - if (m_mode->GetSelection() == wxNOT_FOUND) { - wxMessageDialog dialog(this, _("Mode is not set"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - if (m_ack->GetSelection() == wxNOT_FOUND) { - wxMessageDialog dialog(this, _("Ack is not set"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - if (m_restriction->GetSelection() == wxNOT_FOUND) { - wxMessageDialog dialog(this, _("Restrict is not set"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - if (m_rpt1Validation->GetSelection() == wxNOT_FOUND) { - wxMessageDialog dialog(this, _("RPT1 Validation is not set"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - if (m_dtmfBlanking->GetSelection() == wxNOT_FOUND) { - wxMessageDialog dialog(this, _("DTMF Blanking is not set"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - if (m_errorReply->GetSelection() == wxNOT_FOUND) { - wxMessageDialog dialog(this, _("Error Reply is not set"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - return true; -} - -wxString CDStarRepeaterConfigCallsignSet::getCallsign() const -{ - wxString callsign = m_callsign->GetValue().MakeUpper(); - wxString suffix = m_suffix->GetStringSelection(); - - callsign.Append(wxT(" ")); - callsign.Truncate(LONG_CALLSIGN_LENGTH - 1U); - callsign.Append(suffix); - - return callsign; -} - -wxString CDStarRepeaterConfigCallsignSet::getGateway() const -{ - wxString gateway = m_gateway->GetValue().MakeUpper(); - - gateway.Append(wxT(" ")); - gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U); - gateway.Append(wxT("G")); - - return gateway; -} - -DSTAR_MODE CDStarRepeaterConfigCallsignSet::getMode() const -{ - int n = m_mode->GetCurrentSelection(); - - return DSTAR_MODE(n); -} - -ACK_TYPE CDStarRepeaterConfigCallsignSet::getAck() const -{ - int n = m_ack->GetCurrentSelection(); - if (n == wxNOT_FOUND) - return AT_BER; - - return ACK_TYPE(n); -} - -bool CDStarRepeaterConfigCallsignSet::getRestriction() const -{ - int n = m_restriction->GetCurrentSelection(); - - return n == 1; -} - -void CDStarRepeaterConfigCallsignSet::onMode(wxCommandEvent &event) -{ - DSTAR_MODE mode = DSTAR_MODE(event.GetSelection()); - - switch (mode) { - case MODE_GATEWAY: - m_ack->SetSelection(0); // Ack off - m_rpt1Validation->SetSelection(1); // RPT1 Validation on - m_ack->Disable(); - m_rpt1Validation->Disable(); - break; - - case MODE_TXONLY: - case MODE_RXONLY: - case MODE_TXANDRX: - m_ack->SetSelection(0); // Ack off - m_ack->Disable(); - break; - - default: - m_ack->Enable(); - m_rpt1Validation->Enable(); - break; - } -} - -bool CDStarRepeaterConfigCallsignSet::getRPT1Validation() const -{ - int n = m_rpt1Validation->GetCurrentSelection(); - - return n == 1; -} - -bool CDStarRepeaterConfigCallsignSet::getDTMFBlanking() const -{ - int n = m_dtmfBlanking->GetCurrentSelection(); - - return n == 1; -} - -bool CDStarRepeaterConfigCallsignSet::getErrorReply() const -{ - int n = m_errorReply->GetCurrentSelection(); - - return n == 1; -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.h b/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.h deleted file mode 100644 index 4190223..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigCallsignSet.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2011,2013 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigCallsignSet_H -#define DStarRepeaterConfigCallsignSet_H - -#include "CallsignTextCtrl.h" -#include "DStarDefines.h" - -#include - -class CDStarRepeaterConfigCallsignSet : public wxPanel { -public: - CDStarRepeaterConfigCallsignSet(wxWindow* parent, int id, const wxString& title, const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply); - virtual ~CDStarRepeaterConfigCallsignSet(); - - virtual bool Validate(); - - virtual wxString getCallsign() const; - virtual wxString getGateway() const; - virtual DSTAR_MODE getMode() const; - virtual ACK_TYPE getAck() const; - virtual bool getRestriction() const; - virtual bool getRPT1Validation() const; - virtual bool getDTMFBlanking() const; - virtual bool getErrorReply() const; - - virtual void onMode(wxCommandEvent& event); - -private: - wxString m_title; - CCallsignTextCtrl* m_callsign; - CCallsignTextCtrl* m_gateway; - wxChoice* m_suffix; - wxChoice* m_mode; - wxChoice* m_ack; - wxChoice* m_restriction; - wxChoice* m_rpt1Validation; - wxChoice* m_dtmfBlanking; - wxChoice* m_errorReply; - - DECLARE_EVENT_TABLE() -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.cpp b/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.cpp deleted file mode 100644 index 6b15ee5..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2009-2012,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigControl1Set.h" -#include "DStarDefines.h" - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH1 = 120U; -const unsigned int CONTROL_WIDTH2 = 200U; - -CDStarRepeaterConfigControl1Set::CDStarRepeaterConfigControl1Set(wxWindow* parent, int id, const wxString& title, bool enabled, const wxString& rpt1Callsign, const wxString& rpt2Callsign, const wxString& shutdown, const wxString& startup, const wxString& status1, const wxString& status2, const wxString& status3, const wxString& status4, const wxString& status5, const wxString& output1, const wxString& output2, const wxString& output3, const wxString& output4) : -wxPanel(parent, id), -m_title(title), -m_enabled(NULL), -m_rpt1Callsign(NULL), -m_rpt2Callsign(NULL), -m_shutdown(NULL), -m_startup(NULL), -m_status1(NULL), -m_status2(NULL), -m_status3(NULL), -m_status4(NULL), -m_status5(NULL), -m_output1(NULL), -m_output2(NULL), -m_output3(NULL), -m_output4(NULL) -{ - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* controlLabel = new wxStaticText(this, -1, _("Control")); - sizer->Add(controlLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_enabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_enabled->Append(_("Disabled")); - m_enabled->Append(_("Enabled")); - sizer->Add(m_enabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_enabled->SetSelection(enabled ? 1 : 0); - - wxStaticText* rpt1CallsignLabel = new wxStaticText(this, -1, _("RPT1 Callsign")); - sizer->Add(rpt1CallsignLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_rpt1Callsign = new CCallsignTextCtrl(this, -1, rpt1Callsign, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_rpt1Callsign->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_rpt1Callsign, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* rpt2CallsignLabel = new wxStaticText(this, -1, _("RPT2 Callsign")); - sizer->Add(rpt2CallsignLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_rpt2Callsign = new CCallsignTextCtrl(this, -1, rpt2Callsign, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_rpt2Callsign->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_rpt2Callsign, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* shutdownLabel = new wxStaticText(this, -1, _("Shutdown")); - sizer->Add(shutdownLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_shutdown = new CCallsignTextCtrl(this, -1, shutdown, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_shutdown->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_shutdown, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* startupLabel = new wxStaticText(this, -1, _("Startup")); - sizer->Add(startupLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_startup = new CCallsignTextCtrl(this, -1, startup, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_startup->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_startup, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* status1Label = new wxStaticText(this, -1, _("Status 1")); - sizer->Add(status1Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_status1 = new CCallsignTextCtrl(this, -1, status1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_status1->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_status1, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* status2Label = new wxStaticText(this, -1, _("Status 2")); - sizer->Add(status2Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_status2 = new CCallsignTextCtrl(this, -1, status2, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_status2->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_status2, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* status3Label = new wxStaticText(this, -1, _("Status 3")); - sizer->Add(status3Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_status3 = new CCallsignTextCtrl(this, -1, status3, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_status3->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_status3, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* status4Label = new wxStaticText(this, -1, _("Status 4")); - sizer->Add(status4Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_status4 = new CCallsignTextCtrl(this, -1, status4, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_status4->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_status4, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* status5Label = new wxStaticText(this, -1, _("Status 5")); - sizer->Add(status5Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_status5 = new CCallsignTextCtrl(this, -1, status5, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_status5->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_status5, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* output1Label = new wxStaticText(this, -1, _("Output 1")); - sizer->Add(output1Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_output1 = new CCallsignTextCtrl(this, -1, output1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_output1->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_output1, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* output2Label = new wxStaticText(this, -1, _("Output 2")); - sizer->Add(output2Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_output2 = new CCallsignTextCtrl(this, -1, output2, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_output2->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_output2, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* output3Label = new wxStaticText(this, -1, _("Output 3")); - sizer->Add(output3Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_output3 = new CCallsignTextCtrl(this, -1, output3, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_output3->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_output3, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* output4Label = new wxStaticText(this, -1, _("Output 4")); - sizer->Add(output4Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_output4 = new CCallsignTextCtrl(this, -1, output4, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_output4->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_output4, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - SetAutoLayout(true); - - sizer->Fit(this); - sizer->SetSizeHints(this); - - SetSizer(sizer); -} - - -CDStarRepeaterConfigControl1Set::~CDStarRepeaterConfigControl1Set() -{ -} - -bool CDStarRepeaterConfigControl1Set::Validate() -{ - return m_enabled->GetCurrentSelection() != wxNOT_FOUND; -} - -bool CDStarRepeaterConfigControl1Set::getEnabled() const -{ - int n = m_enabled->GetCurrentSelection(); - - return n == 1; -} - -wxString CDStarRepeaterConfigControl1Set::getRPT1Callsign() const -{ - return m_rpt1Callsign->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getRPT2Callsign() const -{ - return m_rpt2Callsign->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getShutdown() const -{ - return m_shutdown->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getStartup() const -{ - return m_startup->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getStatus1() const -{ - return m_status1->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getStatus2() const -{ - return m_status2->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getStatus3() const -{ - return m_status3->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getStatus4() const -{ - return m_status4->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getStatus5() const -{ - return m_status5->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getOutput1() const -{ - return m_output1->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getOutput2() const -{ - return m_output2->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getOutput3() const -{ - return m_output3->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl1Set::getOutput4() const -{ - return m_output4->GetValue().MakeUpper(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.h b/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.h deleted file mode 100644 index c290aa3..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigControl1Set.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2009-2012,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigControl1Set_H -#define DStarRepeaterConfigControl1Set_H - -#include "CallsignTextCtrl.h" - -#include - -class CDStarRepeaterConfigControl1Set : public wxPanel { -public: - CDStarRepeaterConfigControl1Set(wxWindow* parent, int id, const wxString& title, bool enabled, - const wxString& rpt1Callsign, const wxString& rpt2Callsign, const wxString& shutdown, - const wxString& startup, const wxString& status1, const wxString& status2, const wxString& status3, - const wxString& status4, const wxString& status5, const wxString& output1, const wxString& output2, - const wxString& output3, const wxString& output4); - virtual ~CDStarRepeaterConfigControl1Set(); - - virtual bool Validate(); - - virtual bool getEnabled() const; - - virtual wxString getRPT1Callsign() const; - virtual wxString getRPT2Callsign() const; - - virtual wxString getShutdown() const; - virtual wxString getStartup() const; - - virtual wxString getStatus1() const; - virtual wxString getStatus2() const; - virtual wxString getStatus3() const; - virtual wxString getStatus4() const; - virtual wxString getStatus5() const; - - virtual wxString getOutput1() const; - virtual wxString getOutput2() const; - virtual wxString getOutput3() const; - virtual wxString getOutput4() const; - -private: - wxString m_title; - wxChoice* m_enabled; - CCallsignTextCtrl* m_rpt1Callsign; - CCallsignTextCtrl* m_rpt2Callsign; - CCallsignTextCtrl* m_shutdown; - CCallsignTextCtrl* m_startup; - CCallsignTextCtrl* m_status1; - CCallsignTextCtrl* m_status2; - CCallsignTextCtrl* m_status3; - CCallsignTextCtrl* m_status4; - CCallsignTextCtrl* m_status5; - CCallsignTextCtrl* m_output1; - CCallsignTextCtrl* m_output2; - CCallsignTextCtrl* m_output3; - CCallsignTextCtrl* m_output4; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.cpp b/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.cpp deleted file mode 100644 index e6198ba..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2009,2010,2012,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigControl2Set.h" -#include "DStarDefines.h" - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH1 = 120U; -const unsigned int CONTROL_WIDTH2 = 200U; - -CDStarRepeaterConfigControl2Set::CDStarRepeaterConfigControl2Set(wxWindow* parent, int id, const wxString& title, const wxString& command1, const wxString& command1Line, const wxString& command2, const wxString& command2Line, const wxString& command3, const wxString& command3Line, const wxString& command4, const wxString& command4Line, const wxString& command5, const wxString& command5Line, const wxString& command6, const wxString& command6Line) : -wxPanel(parent, id), -m_title(title), -m_command1(NULL), -m_command1Line(NULL), -m_command2(NULL), -m_command2Line(NULL), -m_command3(NULL), -m_command3Line(NULL), -m_command4(NULL), -m_command4Line(NULL), -m_command5(NULL), -m_command5Line(NULL), -m_command6(NULL), -m_command6Line(NULL) -{ - wxFlexGridSizer* sizer = new wxFlexGridSizer(3); - - wxStaticText* command1Label = new wxStaticText(this, -1, _("Command 1")); - sizer->Add(command1Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command1 = new CCallsignTextCtrl(this, -1, command1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_command1->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_command1, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command1Line = new wxTextCtrl(this, -1, command1Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); - sizer->Add(m_command1Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* command2Label = new wxStaticText(this, -1, _("Command 2")); - sizer->Add(command2Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command2 = new CCallsignTextCtrl(this, -1, command2, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_command2->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_command2, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command2Line = new wxTextCtrl(this, -1, command2Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); - sizer->Add(m_command2Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* command3Label = new wxStaticText(this, -1, _("Command 3")); - sizer->Add(command3Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command3 = new CCallsignTextCtrl(this, -1, command3, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_command3->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_command3, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command3Line = new wxTextCtrl(this, -1, command3Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); - sizer->Add(m_command3Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* command4Label = new wxStaticText(this, -1, _("Command 4")); - sizer->Add(command4Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command4 = new CCallsignTextCtrl(this, -1, command4, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_command4->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_command4, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command4Line = new wxTextCtrl(this, -1, command4Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); - sizer->Add(m_command4Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* command5Label = new wxStaticText(this, -1, _("Command 5")); - sizer->Add(command5Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command5 = new CCallsignTextCtrl(this, -1, command5, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_command5->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_command5, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command5Line = new wxTextCtrl(this, -1, command5Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); - sizer->Add(m_command5Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* command6Label = new wxStaticText(this, -1, _("Command 6")); - sizer->Add(command6Label, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command6 = new CCallsignTextCtrl(this, -1, command6, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_command6->SetMaxLength(LONG_CALLSIGN_LENGTH); - sizer->Add(m_command6, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_command6Line = new wxTextCtrl(this, -1, command6Line, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); - sizer->Add(m_command6Line, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - SetAutoLayout(true); - - sizer->Fit(this); - sizer->SetSizeHints(this); - - SetSizer(sizer); -} - - -CDStarRepeaterConfigControl2Set::~CDStarRepeaterConfigControl2Set() -{ -} - -bool CDStarRepeaterConfigControl2Set::Validate() -{ - return true; -} - -wxString CDStarRepeaterConfigControl2Set::getCommand1() const -{ - return m_command1->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl2Set::getCommand1Line() const -{ - return m_command1Line->GetValue(); -} - -wxString CDStarRepeaterConfigControl2Set::getCommand2() const -{ - return m_command2->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl2Set::getCommand2Line() const -{ - return m_command2Line->GetValue(); -} - -wxString CDStarRepeaterConfigControl2Set::getCommand3() const -{ - return m_command3->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl2Set::getCommand3Line() const -{ - return m_command3Line->GetValue(); -} - -wxString CDStarRepeaterConfigControl2Set::getCommand4() const -{ - return m_command4->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl2Set::getCommand4Line() const -{ - return m_command4Line->GetValue(); -} - -wxString CDStarRepeaterConfigControl2Set::getCommand5() const -{ - return m_command5->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl2Set::getCommand5Line() const -{ - return m_command5Line->GetValue(); -} - -wxString CDStarRepeaterConfigControl2Set::getCommand6() const -{ - return m_command6->GetValue().MakeUpper(); -} - -wxString CDStarRepeaterConfigControl2Set::getCommand6Line() const -{ - return m_command6Line->GetValue(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.h b/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.h deleted file mode 100644 index 49bdc3a..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigControl2Set.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2009,2010,2012,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigControl2Set_H -#define DStarRepeaterConfigControl2Set_H - -#include "CallsignTextCtrl.h" - -#include - -class CDStarRepeaterConfigControl2Set : public wxPanel { -public: - CDStarRepeaterConfigControl2Set(wxWindow* parent, int id, const wxString& title, const wxString& command1, - const wxString& command1Line, const wxString& command2, const wxString& command2Line, - const wxString& command3, const wxString& command3Line, const wxString& command4, - const wxString& command4Line, const wxString& command5, const wxString& command5Line, - const wxString& command6, const wxString& command6Line); - virtual ~CDStarRepeaterConfigControl2Set(); - - virtual bool Validate(); - - virtual wxString getCommand1() const; - virtual wxString getCommand1Line() const; - - virtual wxString getCommand2() const; - virtual wxString getCommand2Line() const; - - virtual wxString getCommand3() const; - virtual wxString getCommand3Line() const; - - virtual wxString getCommand4() const; - virtual wxString getCommand4Line() const; - - virtual wxString getCommand5() const; - virtual wxString getCommand5Line() const; - - virtual wxString getCommand6() const; - virtual wxString getCommand6Line() const; - -private: - wxString m_title; - CCallsignTextCtrl* m_command1; - wxTextCtrl* m_command1Line; - CCallsignTextCtrl* m_command2; - wxTextCtrl* m_command2Line; - CCallsignTextCtrl* m_command3; - wxTextCtrl* m_command3Line; - CCallsignTextCtrl* m_command4; - wxTextCtrl* m_command4Line; - CCallsignTextCtrl* m_command5; - wxTextCtrl* m_command5Line; - CCallsignTextCtrl* m_command6; - wxTextCtrl* m_command6Line; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.cpp deleted file mode 100644 index e22e012..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2012-2015 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigControllerSet.h" -#include "SerialPortSelector.h" - -const unsigned int CONTROL_WIDTH1 = 150U; -const unsigned int CONTROL_WIDTH2 = 300U; -const unsigned int CONTROL_WIDTH3 = 80U; - -const unsigned int BORDER_SIZE = 5U; - -CDStarRepeaterConfigControllerSet::CDStarRepeaterConfigControllerSet(wxWindow* parent, int id, const wxString& title, const wxString& type, unsigned int config, bool pttInvert, unsigned int time) : -wxPanel(parent, id), -m_title(title), -m_type(NULL), -m_config(NULL), -m_pttInvert(NULL), -m_time(NULL) -{ - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* typeLabel = new wxStaticText(this, -1, _("Type")); - sizer->Add(typeLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_type = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - - m_type->Append(_("None")); - -#if defined(GPIO) - m_type->Append(wxT("GPIO")); - m_type->Append(wxT("UDRC")); -#endif - - m_type->Append(wxT("Velleman K8055 - 0")); - m_type->Append(wxT("Velleman K8055 - 1")); - m_type->Append(wxT("Velleman K8055 - 2")); - m_type->Append(wxT("Velleman K8055 - 3")); - - // Add the URI USB - m_type->Append(wxT("URI USB - 1")); - m_type->Append(wxT("URI USB - 2")); - m_type->Append(wxT("URI USB - 3")); - m_type->Append(wxT("URI USB - 4")); - m_type->Append(wxT("URI USB - 5")); - m_type->Append(wxT("URI USB - 6")); - - // Add the Serial ports - wxArrayString serialDevs = CSerialPortSelector::getDevices(); - for (size_t i = 0U; i < serialDevs.GetCount(); i++) - m_type->Append(wxT("Serial - ") + serialDevs.Item(i)); - - // Add the Arduino ports - serialDevs = CSerialPortSelector::getDevices(); - for (size_t i = 0U; i < serialDevs.GetCount(); i++) - m_type->Append(wxT("Arduino - ") + serialDevs.Item(i)); - - sizer->Add(m_type, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - if (type.IsEmpty()) - m_type->SetSelection(0); - else - m_type->SetStringSelection(type); - - wxStaticText* configLabel = new wxStaticText(this, -1, _("Config")); - sizer->Add(configLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_config = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH3, -1)); - m_config->Append("1"); - m_config->Append("2"); - m_config->Append("3"); - m_config->Append("4"); - m_config->Append("5"); - sizer->Add(m_config, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_config->SetSelection(config - 1); - - wxStaticText* pttInvertLabel = new wxStaticText(this, -1, _("PTT Inversion")); - sizer->Add(pttInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_pttInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH3, -1)); - m_pttInvert->Append(_("Off")); - m_pttInvert->Append(_("On")); - sizer->Add(m_pttInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_pttInvert->SetSelection(pttInvert ? 1 : 0); - - wxStaticText* timeLabel = new wxStaticText(this, -1, _("Time (secs)")); - sizer->Add(timeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_time = new wxSlider(this, -1, time, 0, 300, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_time, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - SetAutoLayout(true); - - sizer->Fit(this); - sizer->SetSizeHints(this); - - SetSizer(sizer); -} - - -CDStarRepeaterConfigControllerSet::~CDStarRepeaterConfigControllerSet() -{ -} - -bool CDStarRepeaterConfigControllerSet::Validate() -{ - if (m_type->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_config->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_pttInvert->GetCurrentSelection() == wxNOT_FOUND) - return false; - - return true; -} - -wxString CDStarRepeaterConfigControllerSet::getType() const -{ - wxString type = m_type->GetStringSelection(); - - if (type.IsSameAs(_("None"))) - return wxEmptyString; - else - return type; -} - -unsigned int CDStarRepeaterConfigControllerSet::getConfig() const -{ - int n = m_config->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return 1U; - else - return n + 1U; -} - -bool CDStarRepeaterConfigControllerSet::getPTTInvert() const -{ - int n = m_pttInvert->GetCurrentSelection(); - - return n == 1; -} - -unsigned int CDStarRepeaterConfigControllerSet::getTime() const -{ - return m_time->GetValue(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.h b/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.h deleted file mode 100644 index 5c45df1..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigControllerSet.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2012,2013,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigControllerSet_H -#define DStarRepeaterConfigControllerSet_H - -#include - -class CDStarRepeaterConfigControllerSet : public wxPanel { -public: - CDStarRepeaterConfigControllerSet(wxWindow* parent, int id, const wxString& title, const wxString& type, unsigned int config, bool pttInvert, unsigned int time); - virtual ~CDStarRepeaterConfigControllerSet(); - - virtual bool Validate(); - - virtual wxString getType() const; - - virtual unsigned int getConfig() const; - - virtual bool getPTTInvert() const; - - virtual unsigned int getTime() const; - -private: - wxString m_title; - wxChoice* m_type; - wxChoice* m_config; - wxChoice* m_pttInvert; - wxSlider* m_time; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.cpp deleted file mode 100644 index c2515c7..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2011-2016 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigDVAPSet.h" -#include "SerialPortSelector.h" - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH = 100U; -const unsigned int NAME_WIDTH = 200U; - -CDStarRepeaterConfigDVAPSet::CDStarRepeaterConfigDVAPSet(wxWindow* parent, int id, const wxString& port, unsigned int frequency, int power, int squelch) : -wxDialog(parent, id, wxString(_("DVAP Modem Settings"))), -m_port(NULL), -m_band(NULL), -m_frequency(NULL), -m_power(NULL), -m_squelch(NULL) -{ - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); - sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_port = new wxChoice(this, -1, wxDefaultPosition, wxSize(NAME_WIDTH, -1)); - sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxArrayString ports = CSerialPortSelector::getDevices(); - for (unsigned int i = 0U; i < ports.GetCount(); i++) - m_port->Append(ports.Item(i)); - bool res = m_port->SetStringSelection(port); - if (!res) - m_port->SetSelection(0); - - wxStaticText* bandLabel = new wxStaticText(this, -1, _("Band")); - sizer->Add(bandLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_band = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - m_band->Append(wxT("144 MHz")); - m_band->Append(wxT("220 MHz")); - m_band->Append(wxT("430 MHz")); - sizer->Add(m_band, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - if (frequency > 400000000U) - m_band->SetSelection(2); - else if (frequency > 200000000U) - m_band->SetSelection(1); - else - m_band->SetSelection(0); - - wxStaticText* freqLabel = new wxStaticText(this, -1, _("Frequency (Hz)")); - sizer->Add(freqLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxString text; - text.Printf(wxT("%u"), frequency); - - m_frequency = new wxTextCtrl(this, -1, text, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - sizer->Add(m_frequency, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* powerLabel = new wxStaticText(this, -1, _("Power (dBm)")); - sizer->Add(powerLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_power = new wxSpinCtrl(this, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1), wxSP_ARROW_KEYS, -12, 10, power); - sizer->Add(m_power, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* squelchLabel = new wxStaticText(this, -1, _("Squelch (dBm)")); - sizer->Add(squelchLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_squelch = new wxSpinCtrl(this, -1, wxEmptyString, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1), wxSP_ARROW_KEYS, -128, -45, squelch); - sizer->Add(m_squelch, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - topSizer->Add(sizer); - - topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - SetAutoLayout(true); - - topSizer->Fit(this); - topSizer->SetSizeHints(this); - - SetSizer(topSizer); -} - -CDStarRepeaterConfigDVAPSet::~CDStarRepeaterConfigDVAPSet() -{ -} - -bool CDStarRepeaterConfigDVAPSet::Validate() -{ - if (m_port->GetCurrentSelection() == wxNOT_FOUND) - return false; - - unsigned int freq = getFrequency(); - - int n = m_band->GetCurrentSelection(); - switch (n) { - case 0: - if (freq < 144000000U || freq > 148000000U) { - wxMessageDialog dialog(this, _("The Frequency is out of range"), _("DVAP Modem Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - break; - - case 1: - if (freq < 220000000U || freq > 225000000U) { - wxMessageDialog dialog(this, _("The Frequency is out of range"), _("DVAP Modem Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - break; - - case 2: - if (freq < 420000000U || freq > 450000000U) { - wxMessageDialog dialog(this, _("The Frequency is out of range"), _("DVAP Modem Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - break; - - default: - return false; - } - - return true; -} - -wxString CDStarRepeaterConfigDVAPSet::getPort() const -{ - return m_port->GetStringSelection(); -} - -unsigned int CDStarRepeaterConfigDVAPSet::getFrequency() const -{ - wxString hz = m_frequency->GetValue(); - - unsigned long frequency; - hz.ToULong(&frequency); - - return frequency; -} - -int CDStarRepeaterConfigDVAPSet::getPower() const -{ - int power = m_power->GetValue(); - - return power; -} - -int CDStarRepeaterConfigDVAPSet::getSquelch() const -{ - int power = m_squelch->GetValue(); - - return power; -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.h b/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.h deleted file mode 100644 index 338a2ec..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigDVAPSet.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigDVAPSet_H -#define DStarRepeaterConfigDVAPSet_H - -#include "DStarRepeaterConfigDefs.h" -#include "DStarDefines.h" - -#include -#include - -class CDStarRepeaterConfigDVAPSet : public wxDialog { -public: - CDStarRepeaterConfigDVAPSet(wxWindow* parent, int id, const wxString& port, unsigned int frequency, int power, int squelch); - virtual ~CDStarRepeaterConfigDVAPSet(); - - virtual bool Validate(); - - virtual wxString getPort() const; - virtual unsigned int getFrequency() const; - virtual int getPower() const; - virtual int getSquelch() const; - -private: - wxChoice* m_port; - wxChoice* m_band; - wxTextCtrl* m_frequency; - wxSpinCtrl* m_power; - wxSpinCtrl* m_squelch; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.cpp deleted file mode 100644 index 9286693..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigDVMegaSet.h" -#include "SerialPortSelector.h" - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH1 = 150U; -const unsigned int CONTROL_WIDTH2 = 300U; - -const unsigned int PORT_LENGTH = 5U; - -const int CHOICE_VARIANT = 8770; - -BEGIN_EVENT_TABLE(CDStarRepeaterConfigDVMegaSet, wxDialog) - EVT_CHOICE(CHOICE_VARIANT, CDStarRepeaterConfigDVMegaSet::onVariant) -END_EVENT_TABLE() - - -CDStarRepeaterConfigDVMegaSet::CDStarRepeaterConfigDVMegaSet(wxWindow* parent, int id, const wxString& port, DVMEGA_VARIANT variant, bool rxInvert, bool txInvert, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power) : -wxDialog(parent, id, wxString(_("DVMEGA Settings"))), -m_port(NULL), -m_variant(NULL), -m_txInvert(NULL), -m_rxInvert(NULL), -m_txDelay(NULL), -m_frequency(NULL), -m_power(NULL) -{ - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); - sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_port = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxArrayString ports = CSerialPortSelector::getDevices(); - for (unsigned int i = 0U; i < ports.GetCount(); i++) - m_port->Append(ports.Item(i)); - - bool found = m_port->SetStringSelection(port); - if (!found) - m_port->SetSelection(0); - - wxStaticText* variantLabel = new wxStaticText(this, -1, _("Variant")); - sizer->Add(variantLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_variant = new wxChoice(this, CHOICE_VARIANT, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_variant->Append(_("Modem")); - m_variant->Append(_("Radio - 2m")); - m_variant->Append(_("Radio - 70cm")); - m_variant->Append(_("Radio - 2m + 70cm")); - sizer->Add(m_variant, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_variant->SetSelection(int(variant)); - - wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); - sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_txInvert->Append(_("Off")); - m_txInvert->Append(_("On")); - sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_txInvert->SetSelection(txInvert ? 1 : 0); - - wxStaticText* rxInvertLabel = new wxStaticText(this, -1, _("RX Inversion")); - sizer->Add(rxInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_rxInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_rxInvert->Append(_("Off")); - m_rxInvert->Append(_("On")); - sizer->Add(m_rxInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_rxInvert->SetSelection(rxInvert ? 1 : 0); - - wxStaticText* freqLabel = new wxStaticText(this, -1, _("Frequency (Hz)")); - sizer->Add(freqLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxString text; - if (rxFrequency == txFrequency) - text.Printf(wxT("%u"), rxFrequency); - else - text.Printf(wxT("%u/%u"), rxFrequency, txFrequency); - - m_frequency = new wxTextCtrl(this, -1, text, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - sizer->Add(m_frequency, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); - sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txDelay = new wxSlider(this, -1, txDelay, 0, 350, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* powerLabel = new wxStaticText(this, -1, _("Power (%)")); - sizer->Add(powerLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_power = new wxSlider(this, -1, power, 1, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_power, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - topSizer->Add(sizer); - - topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - switch (variant) { - case DVMV_RADIO_2M: - case DVMV_RADIO_70CM: - case DVMV_RADIO_2M_70CM: - m_txInvert->Disable(); - m_rxInvert->Disable(); - break; - default: - m_frequency->Disable(); - m_power->Disable(); - break; - } - - SetAutoLayout(true); - - topSizer->Fit(this); - topSizer->SetSizeHints(this); - - SetSizer(topSizer); -} - -CDStarRepeaterConfigDVMegaSet::~CDStarRepeaterConfigDVMegaSet() -{ -} - -bool CDStarRepeaterConfigDVMegaSet::Validate() -{ - if (m_port->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_variant->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_txInvert->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_rxInvert->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_variant->GetSelection() == 1) { - unsigned int frequency = getRXFrequency(); - - if (frequency >= 144000000U && frequency < 148000000U) - return true; - else - return false; - } else if (m_variant->GetSelection() == 2) { - unsigned int frequency = getRXFrequency(); - - if (frequency >= 420000000U && frequency < 450000000U) - return true; - else - return false; - } else if (m_variant->GetSelection() == 3) { - unsigned int rxFrequency = getRXFrequency(); - unsigned int txFrequency = getTXFrequency(); - - if (rxFrequency == txFrequency) { - if ((rxFrequency >= 144000000U && rxFrequency < 148000000U) || - (rxFrequency >= 420000000U && rxFrequency < 450000000U)) - return true; - else - return false; - } else { - bool rx2m = (rxFrequency >= 144000000U && rxFrequency < 148000000U); - bool rx70cm = (rxFrequency >= 420000000U && rxFrequency < 450000000U); - - bool tx2m = (txFrequency >= 144000000U && txFrequency < 148000000U); - bool tx70cm = (txFrequency >= 420000000U && txFrequency < 450000000U); - - if (rx2m && tx70cm) - return true; - else if (rx70cm && tx2m) - return true; - else - return false; - } - } - - return true; -} - -wxString CDStarRepeaterConfigDVMegaSet::getPort() const -{ - int n = m_port->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return wxEmptyString; - - return m_port->GetStringSelection(); -} - -DVMEGA_VARIANT CDStarRepeaterConfigDVMegaSet::getVariant() const -{ - int n = m_variant->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return DVMV_MODEM; - - return DVMEGA_VARIANT(n); -} - -bool CDStarRepeaterConfigDVMegaSet::getRXInvert() const -{ - int n = m_rxInvert->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -bool CDStarRepeaterConfigDVMegaSet::getTXInvert() const -{ - int n = m_txInvert->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -unsigned int CDStarRepeaterConfigDVMegaSet::getTXDelay() const -{ - return (unsigned int)m_txDelay->GetValue(); -} - -unsigned int CDStarRepeaterConfigDVMegaSet::getRXFrequency() const -{ - wxString hz = m_frequency->GetValue(); - - int pos = hz.Find(wxT('/')); - if (pos != wxNOT_FOUND) { - unsigned long frequency; - hz.Left(pos).ToULong(&frequency); - return frequency; - } else { - unsigned long frequency; - hz.ToULong(&frequency); - return frequency; - } -} - -unsigned int CDStarRepeaterConfigDVMegaSet::getTXFrequency() const -{ - DVMEGA_VARIANT variant = getVariant(); - wxString hz = m_frequency->GetValue(); - - int pos = hz.Find(wxT('/')); - if (pos != wxNOT_FOUND && variant == DVMV_RADIO_2M_70CM) { - unsigned long frequency; - hz.Mid(pos + 1).ToULong(&frequency); - return frequency; - } else { - if (pos != wxNOT_FOUND) { - unsigned long frequency; - hz.Left(pos).ToULong(&frequency); - return frequency; - } else { - unsigned long frequency; - hz.ToULong(&frequency); - return frequency; - } - } -} - -unsigned int CDStarRepeaterConfigDVMegaSet::getPower() const -{ - return (unsigned int)m_power->GetValue(); -} - -void CDStarRepeaterConfigDVMegaSet::onVariant(wxCommandEvent &event) -{ - DVMEGA_VARIANT variant = getVariant(); - - switch (variant) { - case DVMV_RADIO_2M: - case DVMV_RADIO_70CM: - case DVMV_RADIO_2M_70CM: - m_txInvert->Disable(); - m_rxInvert->Disable(); - m_frequency->Enable(); - m_power->Enable(); - break; - default: // DVMV_MODEM - m_txInvert->Enable(); - m_rxInvert->Enable(); - m_frequency->Disable(); - m_power->Disable(); - break; - } -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.h b/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.h deleted file mode 100644 index fcea64e..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigDVMegaSet.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigDVMegaSet_H -#define DStarRepeaterConfigDVMegaSet_H - -#include "DStarRepeaterConfigDefs.h" -#include "DStarDefines.h" - -#include - -class CDStarRepeaterConfigDVMegaSet : public wxDialog { -public: - CDStarRepeaterConfigDVMegaSet(wxWindow* parent, int id, const wxString& port, DVMEGA_VARIANT variant, bool rxInvert, bool txInvert, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power); - virtual ~CDStarRepeaterConfigDVMegaSet(); - - virtual bool Validate(); - - virtual wxString getPort() const; - virtual DVMEGA_VARIANT getVariant() const; - virtual bool getRXInvert() const; - virtual bool getTXInvert() const; - virtual unsigned int getTXDelay() const; - virtual unsigned int getRXFrequency() const; - virtual unsigned int getTXFrequency() const; - virtual unsigned int getPower() const; - - virtual void onVariant(wxCommandEvent& event); - -private: - wxChoice* m_port; - wxChoice* m_variant; - wxChoice* m_txInvert; - wxChoice* m_rxInvert; - wxSlider* m_txDelay; - wxTextCtrl* m_frequency; - wxSlider* m_power; - - DECLARE_EVENT_TABLE() -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.cpp b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.cpp deleted file mode 100644 index ae09700..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigDVRPTR1Set.h" -#include "SerialPortSelector.h" - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH1 = 150U; -const unsigned int CONTROL_WIDTH2 = 300U; - -const unsigned int ADDRESS_LENGTH = 15U; -const unsigned int PORT_LENGTH = 5U; - - -CDStarRepeaterConfigDVRPTR1Set::CDStarRepeaterConfigDVRPTR1Set(wxWindow* parent, int id, const wxString& port, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay) : -wxDialog(parent, id, wxString(_("DV-RPTR V1 Settings"))), -m_port(NULL), -m_txInvert(NULL), -m_rxInvert(NULL), -m_channel(NULL), -m_modLevel(NULL), -m_txDelay(NULL) -{ - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); - sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_port = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxArrayString ports = CSerialPortSelector::getDevices(); - for (unsigned int i = 0U; i < ports.GetCount(); i++) - m_port->Append(ports.Item(i)); - - bool found = m_port->SetStringSelection(port); - if (!found) - m_port->SetSelection(0); - - wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); - sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_txInvert->Append(_("Off")); - m_txInvert->Append(_("On")); - sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_txInvert->SetSelection(txInvert ? 1 : 0); - - wxStaticText* rxInvertLabel = new wxStaticText(this, -1, _("RX Inversion")); - sizer->Add(rxInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_rxInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_rxInvert->Append(_("Off")); - m_rxInvert->Append(_("On")); - sizer->Add(m_rxInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_rxInvert->SetSelection(rxInvert ? 1 : 0); - - wxStaticText* channelLabel = new wxStaticText(this, -1, _("Channel")); - sizer->Add(channelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_channel = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_channel->Append(_("A / FSK Pin")); - m_channel->Append(_("B / AFSK Pin")); - sizer->Add(m_channel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_channel->SetSelection(channel ? 1 : 0); - - wxStaticText* modLevelLabel = new wxStaticText(this, -1, _("TX Level (%)")); - sizer->Add(modLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_modLevel = new wxSlider(this, -1, modLevel, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_modLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); - sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txDelay = new wxSlider(this, -1, txDelay, 0, 500, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - topSizer->Add(sizer); - - topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - SetAutoLayout(true); - - topSizer->Fit(this); - topSizer->SetSizeHints(this); - - SetSizer(topSizer); -} - -CDStarRepeaterConfigDVRPTR1Set::~CDStarRepeaterConfigDVRPTR1Set() -{ -} - -bool CDStarRepeaterConfigDVRPTR1Set::Validate() -{ - if (m_port->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_txInvert->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_rxInvert->GetCurrentSelection() == wxNOT_FOUND) - return false; - - return m_channel->GetCurrentSelection() != wxNOT_FOUND; -} - -wxString CDStarRepeaterConfigDVRPTR1Set::getPort() const -{ - int n = m_port->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return wxEmptyString; - - return m_port->GetStringSelection(); -} - -bool CDStarRepeaterConfigDVRPTR1Set::getRXInvert() const -{ - int n = m_rxInvert->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -bool CDStarRepeaterConfigDVRPTR1Set::getTXInvert() const -{ - int n = m_txInvert->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -bool CDStarRepeaterConfigDVRPTR1Set::getChannel() const -{ - int n = m_channel->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -unsigned int CDStarRepeaterConfigDVRPTR1Set::getModLevel() const -{ - return (unsigned int)m_modLevel->GetValue(); -} - -unsigned int CDStarRepeaterConfigDVRPTR1Set::getTXDelay() const -{ - return (unsigned int)m_txDelay->GetValue(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.h b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.h deleted file mode 100644 index 66c8ed9..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR1Set.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigDVRPTR1Set_H -#define DStarRepeaterConfigDVRPTR1Set_H - -#include "DStarRepeaterConfigDefs.h" -#include "DStarDefines.h" - -#include - -class CDStarRepeaterConfigDVRPTR1Set : public wxDialog { -public: - CDStarRepeaterConfigDVRPTR1Set(wxWindow* parent, int id, const wxString& port, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay); - virtual ~CDStarRepeaterConfigDVRPTR1Set(); - - virtual bool Validate(); - - virtual wxString getPort() const; - virtual bool getRXInvert() const; - virtual bool getTXInvert() const; - virtual bool getChannel() const; - virtual unsigned int getModLevel() const; - virtual unsigned int getTXDelay() const; - -private: - wxChoice* m_port; - wxChoice* m_txInvert; - wxChoice* m_rxInvert; - wxChoice* m_channel; - wxSlider* m_modLevel; - wxSlider* m_txDelay; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.cpp b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.cpp deleted file mode 100644 index 71eea73..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigDVRPTR2Set.h" -#include "SerialPortSelector.h" - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH1 = 150U; -const unsigned int CONTROL_WIDTH2 = 300U; - -const unsigned int ADDRESS_LENGTH = 15U; -const unsigned int PORT_LENGTH = 5U; - -const int CHOICE_TYPE = 8750; - -BEGIN_EVENT_TABLE(CDStarRepeaterConfigDVRPTR2Set, wxDialog) - EVT_CHOICE(CHOICE_TYPE, CDStarRepeaterConfigDVRPTR2Set::onConnectionType) -END_EVENT_TABLE() - - -CDStarRepeaterConfigDVRPTR2Set::CDStarRepeaterConfigDVRPTR2Set(wxWindow* parent, int id, CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay) : -wxDialog(parent, id, wxString(_("DV-RPTR V2 Settings"))), -m_connectionType(NULL), -m_usbPort(NULL), -m_address(NULL), -m_port(NULL), -m_txInvert(NULL), -m_modLevel(NULL), -m_txDelay(NULL) -{ - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* connectionLabel = new wxStaticText(this, -1, _("Connection")); - sizer->Add(connectionLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_connectionType = new wxChoice(this, CHOICE_TYPE, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_connectionType->Append(wxT("USB")); - m_connectionType->Append(_("Network")); - sizer->Add(m_connectionType, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_connectionType->SetSelection(connectionType == CT_NETWORK ? 1 : 0); - - wxStaticText* usbPortLabel = new wxStaticText(this, -1, _("USB Port")); - sizer->Add(usbPortLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_usbPort = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - sizer->Add(m_usbPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxArrayString ports = CSerialPortSelector::getDevices(); - for (unsigned int i = 0U; i < ports.GetCount(); i++) - m_usbPort->Append(ports.Item(i)); - - bool found = m_usbPort->SetStringSelection(usbPort); - if (!found) - m_usbPort->SetSelection(0); - - wxStaticText* addressLabel = new wxStaticText(this, -1, _("Address")); - sizer->Add(addressLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_address = new CAddressTextCtrl(this, -1, address, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_address->SetMaxLength(ADDRESS_LENGTH); - sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); - sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxString buffer; - buffer.Printf(wxT("%u"), port); - - m_port = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_port->SetMaxLength(PORT_LENGTH); - sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); - sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_txInvert->Append(_("Off")); - m_txInvert->Append(_("On")); - sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_txInvert->SetSelection(txInvert ? 1 : 0); - - wxStaticText* modLevelLabel = new wxStaticText(this, -1, _("TX Level (%)")); - sizer->Add(modLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_modLevel = new wxSlider(this, -1, modLevel, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_modLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); - sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txDelay = new wxSlider(this, -1, txDelay, 100, 850, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - switch (connectionType) { - case CT_NETWORK: - m_usbPort->Disable(); - m_address->Enable(); - m_port->Enable(); - break; - default: - m_usbPort->Enable(); - m_address->Disable(); - m_port->Disable(); - break; - } - - topSizer->Add(sizer); - - topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - SetAutoLayout(true); - - topSizer->Fit(this); - topSizer->SetSizeHints(this); - - SetSizer(topSizer); -} - -CDStarRepeaterConfigDVRPTR2Set::~CDStarRepeaterConfigDVRPTR2Set() -{ -} - -bool CDStarRepeaterConfigDVRPTR2Set::Validate() -{ - if (m_connectionType->GetCurrentSelection() == wxNOT_FOUND) - return false; - - CONNECTION_TYPE type = getConnectionType(); - if (type == CT_USB) { - if (m_usbPort->GetCurrentSelection() == wxNOT_FOUND) - return false; - } else { - unsigned int port = getPort(); - - if (port == 0U || port > 65535U) { - wxMessageDialog dialog(this, _("The Port is not valid"), _("DV-RPTR V2 Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - } - - return m_txInvert->GetCurrentSelection() != wxNOT_FOUND; -} - -CONNECTION_TYPE CDStarRepeaterConfigDVRPTR2Set::getConnectionType() const -{ - int n = m_connectionType->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return CT_USB; - - return n == 1 ? CT_NETWORK : CT_USB; -} - -wxString CDStarRepeaterConfigDVRPTR2Set::getUSBPort() const -{ - int n = m_usbPort->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return wxEmptyString; - - return m_usbPort->GetStringSelection(); -} - -wxString CDStarRepeaterConfigDVRPTR2Set::getAddress() const -{ - return m_address->GetValue(); -} - -unsigned int CDStarRepeaterConfigDVRPTR2Set::getPort() const -{ - unsigned long n; - - m_port->GetValue().ToULong(&n); - - return n; -} - -bool CDStarRepeaterConfigDVRPTR2Set::getTXInvert() const -{ - int n = m_txInvert->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -unsigned int CDStarRepeaterConfigDVRPTR2Set::getModLevel() const -{ - return (unsigned int)m_modLevel->GetValue(); -} - -unsigned int CDStarRepeaterConfigDVRPTR2Set::getTXDelay() const -{ - return (unsigned int)m_txDelay->GetValue(); -} - -void CDStarRepeaterConfigDVRPTR2Set::onConnectionType(wxCommandEvent &event) -{ - int n = event.GetSelection(); - - switch (n) { - case 1: // CT_NETWORK - m_usbPort->Disable(); - m_address->Enable(); - m_port->Enable(); - break; - default: // CT_USB - m_usbPort->Enable(); - m_address->Disable(); - m_port->Disable(); - break; - } -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.h b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.h deleted file mode 100644 index 1198e61..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR2Set.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigDVRPTR2Set_H -#define DStarRepeaterConfigDVRPTR2Set_H - -#include "DStarRepeaterConfigDefs.h" -#include "AddressTextCtrl.h" -#include "DStarDefines.h" -#include "PortTextCtrl.h" - -#include - -class CDStarRepeaterConfigDVRPTR2Set : public wxDialog { -public: - CDStarRepeaterConfigDVRPTR2Set(wxWindow* parent, int id, CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay); - virtual ~CDStarRepeaterConfigDVRPTR2Set(); - - virtual bool Validate(); - - virtual CONNECTION_TYPE getConnectionType() const; - virtual wxString getUSBPort() const; - virtual wxString getAddress() const; - virtual unsigned int getPort() const; - virtual bool getTXInvert() const; - virtual unsigned int getModLevel() const; - virtual unsigned int getTXDelay() const; - - virtual void onConnectionType(wxCommandEvent& event); - -private: - wxChoice* m_connectionType; - wxChoice* m_usbPort; - CAddressTextCtrl* m_address; - CPortTextCtrl* m_port; - wxChoice* m_txInvert; - wxSlider* m_modLevel; - wxSlider* m_txDelay; - - DECLARE_EVENT_TABLE() -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.cpp b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.cpp deleted file mode 100644 index 568eed5..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigDVRPTR3Set.h" -#include "SerialPortSelector.h" - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH1 = 150U; -const unsigned int CONTROL_WIDTH2 = 300U; - -const unsigned int ADDRESS_LENGTH = 15U; -const unsigned int PORT_LENGTH = 5U; - -const int CHOICE_TYPE = 8750; - -BEGIN_EVENT_TABLE(CDStarRepeaterConfigDVRPTR3Set, wxDialog) - EVT_CHOICE(CHOICE_TYPE, CDStarRepeaterConfigDVRPTR3Set::onConnectionType) -END_EVENT_TABLE() - - -CDStarRepeaterConfigDVRPTR3Set::CDStarRepeaterConfigDVRPTR3Set(wxWindow* parent, int id, CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay) : -wxDialog(parent, id, wxString(_("DV-RPTR V3 Settings"))), -m_connectionType(NULL), -m_usbPort(NULL), -m_address(NULL), -m_port(NULL), -m_txInvert(NULL), -m_modLevel(NULL), -m_txDelay(NULL) -{ - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* connectionLabel = new wxStaticText(this, -1, _("Connection")); - sizer->Add(connectionLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_connectionType = new wxChoice(this, CHOICE_TYPE, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_connectionType->Append(wxT("USB")); - m_connectionType->Append(_("Network")); - sizer->Add(m_connectionType, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_connectionType->SetSelection(connectionType == CT_NETWORK ? 1 : 0); - - wxStaticText* usbPortLabel = new wxStaticText(this, -1, _("USB Port")); - sizer->Add(usbPortLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_usbPort = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - sizer->Add(m_usbPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxArrayString ports = CSerialPortSelector::getDevices(); - for (unsigned int i = 0U; i < ports.GetCount(); i++) - m_usbPort->Append(ports.Item(i)); - - bool found = m_usbPort->SetStringSelection(usbPort); - if (!found) - m_usbPort->SetSelection(0); - - wxStaticText* addressLabel = new wxStaticText(this, -1, _("Address")); - sizer->Add(addressLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_address = new CAddressTextCtrl(this, -1, address, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_address->SetMaxLength(ADDRESS_LENGTH); - sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); - sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxString buffer; - buffer.Printf(wxT("%u"), port); - - m_port = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_port->SetMaxLength(PORT_LENGTH); - sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); - sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_txInvert->Append(_("Off")); - m_txInvert->Append(_("On")); - sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_txInvert->SetSelection(txInvert ? 1 : 0); - - wxStaticText* modLevelLabel = new wxStaticText(this, -1, _("TX Level (%)")); - sizer->Add(modLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_modLevel = new wxSlider(this, -1, modLevel, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_modLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); - sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txDelay = new wxSlider(this, -1, txDelay, 100, 850, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - switch (connectionType) { - case CT_NETWORK: - m_usbPort->Disable(); - m_address->Enable(); - m_port->Enable(); - break; - default: - m_usbPort->Enable(); - m_address->Disable(); - m_port->Disable(); - break; - } - - topSizer->Add(sizer); - - topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - SetAutoLayout(true); - - topSizer->Fit(this); - topSizer->SetSizeHints(this); - - SetSizer(topSizer); -} - -CDStarRepeaterConfigDVRPTR3Set::~CDStarRepeaterConfigDVRPTR3Set() -{ -} - -bool CDStarRepeaterConfigDVRPTR3Set::Validate() -{ - if (m_connectionType->GetCurrentSelection() == wxNOT_FOUND) - return false; - - CONNECTION_TYPE type = getConnectionType(); - if (type == CT_USB) { - if (m_usbPort->GetCurrentSelection() == wxNOT_FOUND) - return false; - } else { - unsigned int port = getPort(); - - if (port == 0U || port > 65535U) { - wxMessageDialog dialog(this, _("The Port is not valid"), _("DV-RPTR V2 Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - } - - return m_txInvert->GetCurrentSelection() != wxNOT_FOUND; -} - -CONNECTION_TYPE CDStarRepeaterConfigDVRPTR3Set::getConnectionType() const -{ - int n = m_connectionType->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return CT_USB; - - return n == 1 ? CT_NETWORK : CT_USB; -} - -wxString CDStarRepeaterConfigDVRPTR3Set::getUSBPort() const -{ - int n = m_usbPort->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return wxEmptyString; - - return m_usbPort->GetStringSelection(); -} - -wxString CDStarRepeaterConfigDVRPTR3Set::getAddress() const -{ - return m_address->GetValue(); -} - -unsigned int CDStarRepeaterConfigDVRPTR3Set::getPort() const -{ - unsigned long n; - - m_port->GetValue().ToULong(&n); - - return n; -} - -bool CDStarRepeaterConfigDVRPTR3Set::getTXInvert() const -{ - int n = m_txInvert->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -unsigned int CDStarRepeaterConfigDVRPTR3Set::getModLevel() const -{ - return (unsigned int)m_modLevel->GetValue(); -} - -unsigned int CDStarRepeaterConfigDVRPTR3Set::getTXDelay() const -{ - return (unsigned int)m_txDelay->GetValue(); -} - -void CDStarRepeaterConfigDVRPTR3Set::onConnectionType(wxCommandEvent &event) -{ - int n = event.GetSelection(); - - switch (n) { - case 1: // CT_NETWORK - m_usbPort->Disable(); - m_address->Enable(); - m_port->Enable(); - break; - default: // CT_USB - m_usbPort->Enable(); - m_address->Disable(); - m_port->Disable(); - break; - } -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.h b/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.h deleted file mode 100644 index 10b0718..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigDVRPTR3Set.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigDVRPTR3Set_H -#define DStarRepeaterConfigDVRPTR3Set_H - -#include "DStarRepeaterConfigDefs.h" -#include "AddressTextCtrl.h" -#include "DStarDefines.h" -#include "PortTextCtrl.h" - -#include - -class CDStarRepeaterConfigDVRPTR3Set : public wxDialog { -public: - CDStarRepeaterConfigDVRPTR3Set(wxWindow* parent, int id, CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay); - virtual ~CDStarRepeaterConfigDVRPTR3Set(); - - virtual bool Validate(); - - virtual CONNECTION_TYPE getConnectionType() const; - virtual wxString getUSBPort() const; - virtual wxString getAddress() const; - virtual unsigned int getPort() const; - virtual bool getTXInvert() const; - virtual unsigned int getModLevel() const; - virtual unsigned int getTXDelay() const; - - virtual void onConnectionType(wxCommandEvent& event); - -private: - wxChoice* m_connectionType; - wxChoice* m_usbPort; - CAddressTextCtrl* m_address; - CPortTextCtrl* m_port; - wxChoice* m_txInvert; - wxSlider* m_modLevel; - wxSlider* m_txDelay; - - DECLARE_EVENT_TABLE() -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigDefs.h b/DStarRepeaterConfig/DStarRepeaterConfigDefs.h deleted file mode 100644 index c358199..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigDefs.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigDefs_H -#define DStarRepeaterConfigDefs_H - -#include - -const wxString APPLICATION_NAME = wxT("D-Star Repeater"); -const wxString LOG_BASE_NAME = wxT("DStarRepeater"); -const wxString CONFIG_FILE_NAME = wxT("dstarrepeater"); - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigFrame.cpp b/DStarRepeaterConfig/DStarRepeaterConfigFrame.cpp deleted file mode 100644 index db1debc..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigFrame.cpp +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (C) 2011-2015,2018 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigFrame.h" -#include "Version.h" - -#include -#include - -enum { - Menu_File_Save = 6000 -}; - -BEGIN_EVENT_TABLE(CDStarRepeaterConfigFrame, wxFrame) - EVT_MENU(wxID_EXIT, CDStarRepeaterConfigFrame::onQuit) - EVT_MENU(Menu_File_Save, CDStarRepeaterConfigFrame::onSave) - EVT_MENU(wxID_ABOUT, CDStarRepeaterConfigFrame::onAbout) - - EVT_CLOSE(CDStarRepeaterConfigFrame::onClose) -END_EVENT_TABLE() - -#if defined(__WINDOWS__) -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH = 100U; -const unsigned int LABEL_WIDTH = 70U; -#else -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH = 150U; -const unsigned int LABEL_WIDTH = 70U; -#endif - -CDStarRepeaterConfigFrame::CDStarRepeaterConfigFrame(const wxString& title, const wxString& confDir, const wxString& name) : -wxFrame(NULL, -1, title), -m_config(NULL), -m_callsign(NULL), -m_network(NULL), -m_times(NULL), -m_beacon(NULL), -m_announcement(NULL), -m_modem(NULL), -m_control1(NULL), -m_control2(NULL), -m_controller(NULL) -{ - SetMenuBar(createMenuBar()); - - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - - wxPanel* panel = new wxPanel(this, -1); - - wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); - - wxNotebook* noteBook = new wxNotebook(panel, -1); - -#if defined(__WINDOWS__) - if (confDir.IsEmpty()) - m_config = new CDStarRepeaterConfig(new wxConfig(APPLICATION_NAME), ::wxGetHomeDir(), CONFIG_FILE_NAME, name); - else - m_config = new CDStarRepeaterConfig(new wxConfig(APPLICATION_NAME), confDir, CONFIG_FILE_NAME, name); -#else - if (confDir.IsEmpty()) - m_config = new CDStarRepeaterConfig(wxT(CONF_DIR), CONFIG_FILE_NAME, name); - else - m_config = new CDStarRepeaterConfig(confDir, CONFIG_FILE_NAME, name); -#endif - - wxString callsign, gateway; - DSTAR_MODE mode; - ACK_TYPE ack; - bool restriction, rpt1Validation, dtmfBlanking, errorReply; - m_config->getCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); - - m_callsign = new CDStarRepeaterConfigCallsignSet(noteBook, -1, APPLICATION_NAME, callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); - noteBook->AddPage(m_callsign, _("Callsign"), true); - - wxString gatewayAddress, localAddress, netName; - unsigned int gatewayPort, localPort; - m_config->getNetwork(gatewayAddress, gatewayPort, localAddress, localPort, netName); - - m_network = new CDStarRepeaterConfigNetworkSet(noteBook, -1, APPLICATION_NAME, gatewayAddress, gatewayPort, localAddress, localPort, netName); - noteBook->AddPage(m_network, _("Network"), false); - - unsigned int timeout, ackTime; - m_config->getTimes(timeout, ackTime); - - m_times = new CDStarRepeaterConfigTimesSet(noteBook, -1, APPLICATION_NAME, timeout, ackTime); - noteBook->AddPage(m_times, _("Timers"), false); - - bool announcementEnabled; - unsigned int announcementTime; - wxString announcementRecordRPT1, announcementRecordRPT2; - wxString announcementDeleteRPT1, announcementDeleteRPT2; - m_config->getAnnouncement(announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); - - m_announcement = new CDStarRepeaterConfigAnnouncementSet(noteBook, -1, APPLICATION_NAME, announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); - noteBook->AddPage(m_announcement, _("Announcement"), false); - - unsigned int beaconTime; - wxString beaconText; - bool beaconVoice; - TEXT_LANG language; - m_config->getBeacon(beaconTime, beaconText, beaconVoice, language); - - m_beacon = new CDStarRepeaterConfigBeaconSet(noteBook, -1, APPLICATION_NAME, beaconTime, beaconText, beaconVoice, language); - noteBook->AddPage(m_beacon, _("Beacon"), false); - - wxString modemType; - m_config->getModem(modemType); - - m_modem = new CDStarRepeaterConfigModemSet(noteBook, -1, APPLICATION_NAME, m_config, modemType); - noteBook->AddPage(m_modem, _("Modem"), false); - - bool enabled; - wxString rpt1Callsign, rpt2Callsign; - wxString shutdown, startup; - wxString status1, status2, status3, status4, status5; - wxString command1, command1Line, command2, command2Line; - wxString command3, command3Line, command4, command4Line; - wxString command5, command5Line, command6, command6Line; - wxString output1, output2, output3, output4; - m_config->getControl(enabled, rpt1Callsign, rpt2Callsign, shutdown, startup, status1, status2, status3, status4, status5, command1, command1Line, command2, command2Line, command3, command3Line, command4, command4Line, command5, command5Line, command6, command6Line, output1, output2, output3, output4); - - m_control1 = new CDStarRepeaterConfigControl1Set(noteBook, -1, APPLICATION_NAME, enabled, rpt1Callsign, rpt2Callsign, shutdown, startup, status1, status2, status3, status4, status5, output1, output2, output3, output4); - noteBook->AddPage(m_control1, _("Control 1"), false); - - m_control2 = new CDStarRepeaterConfigControl2Set(noteBook, -1, APPLICATION_NAME, command1, command1Line, command2, command2Line, command3, command3Line, command4, command4Line, command5, command5Line, command6, command6Line); - noteBook->AddPage(m_control2, _("Control 2"), false); - - wxString controllerType; - unsigned int serialConfig, activeHangTime; - bool pttInvert; - m_config->getController(controllerType, serialConfig, pttInvert, activeHangTime); - - m_controller = new CDStarRepeaterConfigControllerSet(noteBook, -1, APPLICATION_NAME, controllerType, serialConfig, pttInvert, activeHangTime); - noteBook->AddPage(m_controller, _("Controller"), false); - - sizer->Add(noteBook, 0, wxEXPAND | wxALL, BORDER_SIZE); - - panel->SetSizer(sizer); - - mainSizer->Add(panel, 0, wxEXPAND | wxALL, BORDER_SIZE); - - mainSizer->SetSizeHints(this); - - SetSizer(mainSizer); -} - -CDStarRepeaterConfigFrame::~CDStarRepeaterConfigFrame() -{ - delete m_config; -} - -wxMenuBar* CDStarRepeaterConfigFrame::createMenuBar() -{ - wxMenu* fileMenu = new wxMenu(); - fileMenu->Append(Menu_File_Save, _("Save")); - fileMenu->AppendSeparator(); - fileMenu->Append(wxID_EXIT, _("Exit")); - - wxMenu* helpMenu = new wxMenu(); - helpMenu->Append(wxID_ABOUT, _("About D-Star Repeater Config")); - - wxMenuBar* menuBar = new wxMenuBar(); - menuBar->Append(fileMenu, _("File")); - menuBar->Append(helpMenu, _("Help")); - - return menuBar; -} - -void CDStarRepeaterConfigFrame::onQuit(wxCommandEvent& event) -{ - Close(false); -} - -void CDStarRepeaterConfigFrame::onClose(wxCloseEvent& event) -{ - Destroy(); -} - -void CDStarRepeaterConfigFrame::onAbout(wxCommandEvent& event) -{ - wxAboutDialogInfo info; - info.AddDeveloper(wxT("Jonathan Naylor, G4KLX")); - info.SetCopyright(wxT("(C) 2013-2018 using GPL v2 or later")); - info.SetName(APPLICATION_NAME); - info.SetVersion(VERSION); - info.SetDescription(_("This program configures the D-Star Repeater.")); - - ::wxAboutBox(info); -} - -void CDStarRepeaterConfigFrame::onSave(wxCommandEvent& event) -{ - if (!m_callsign->Validate() || !m_network->Validate() || !m_times->Validate() || !m_beacon->Validate() || - !m_announcement->Validate() || !m_modem->Validate() || !m_control1->Validate() || !m_control2->Validate() || - !m_controller->Validate()) - return; - - wxString callsign = m_callsign->getCallsign(); - wxString gateway = m_callsign->getGateway(); - DSTAR_MODE mode = m_callsign->getMode(); - ACK_TYPE ack = m_callsign->getAck(); - bool restriction = m_callsign->getRestriction(); - bool rpt1Validation = m_callsign->getRPT1Validation(); - bool dtmfBlanking = m_callsign->getDTMFBlanking(); - bool errorReply = m_callsign->getErrorReply(); - m_config->setCallsign(callsign, gateway, mode, ack, restriction, rpt1Validation, dtmfBlanking, errorReply); - - wxString gatewayAddress = m_network->getGatewayAddress(); - unsigned int gatewayPort = m_network->getGatewayPort(); - wxString localAddress = m_network->getLocalAddress(); - unsigned int localPort = m_network->getLocalPort(); - wxString netName = m_network->getName(); - m_config->setNetwork(gatewayAddress, gatewayPort, localAddress, localPort, netName); - - unsigned int timeout = m_times->getTimeout(); - unsigned int ackTime = m_times->getAckTime(); - m_config->setTimes(timeout, ackTime); - - unsigned int beaconTime = m_beacon->getTime(); - wxString beaconText = m_beacon->getText(); - bool beaconVoice = m_beacon->getVoice(); - TEXT_LANG language = m_beacon->getLanguage(); - m_config->setBeacon(beaconTime, beaconText, beaconVoice, language); - - bool announcementEnabled = m_announcement->getEnabled(); - unsigned int announcementTime = m_announcement->getTime(); - wxString announcementRecordRPT1 = m_announcement->getRecordRPT1(); - wxString announcementRecordRPT2 = m_announcement->getRecordRPT2(); - wxString announcementDeleteRPT1 = m_announcement->getDeleteRPT1(); - wxString announcementDeleteRPT2 = m_announcement->getDeleteRPT2(); - m_config->setAnnouncement(announcementEnabled, announcementTime, announcementRecordRPT1, announcementRecordRPT2, announcementDeleteRPT1, announcementDeleteRPT2); - - wxString modemType = m_modem->getType(); - m_config->setModem(modemType); - - bool enabled = m_control1->getEnabled(); - wxString rpt1Callsign = m_control1->getRPT1Callsign(); - wxString rpt2Callsign = m_control1->getRPT2Callsign(); - wxString shutdown = m_control1->getShutdown(); - wxString startup = m_control1->getStartup(); - wxString status1 = m_control1->getStatus1(); - wxString status2 = m_control1->getStatus2(); - wxString status3 = m_control1->getStatus3(); - wxString status4 = m_control1->getStatus4(); - wxString status5 = m_control1->getStatus5(); - wxString output1 = m_control1->getOutput1(); - wxString output2 = m_control1->getOutput2(); - wxString output3 = m_control1->getOutput3(); - wxString output4 = m_control1->getOutput4(); - wxString command1 = m_control2->getCommand1(); - wxString command1Line = m_control2->getCommand1Line(); - wxString command2 = m_control2->getCommand2(); - wxString command2Line = m_control2->getCommand2Line(); - wxString command3 = m_control2->getCommand3(); - wxString command3Line = m_control2->getCommand3Line(); - wxString command4 = m_control2->getCommand4(); - wxString command4Line = m_control2->getCommand4Line(); - wxString command5 = m_control2->getCommand5(); - wxString command5Line = m_control2->getCommand5Line(); - wxString command6 = m_control2->getCommand6(); - wxString command6Line = m_control2->getCommand6Line(); - m_config->setControl(enabled, rpt1Callsign, rpt2Callsign, shutdown, startup, status1, status2, status3, status4, status5, command1, command1Line, command2, command2Line, command3, command3Line, command4, command4Line, command5, command5Line, command6, command6Line, output1, output2, output3, output4); - - wxString controllerType = m_controller->getType(); - unsigned int serialConfig = m_controller->getConfig(); - bool pttInvert = m_controller->getPTTInvert(); - unsigned int activeHangTime = m_controller->getTime(); - m_config->setController(controllerType, serialConfig, pttInvert, activeHangTime); - - bool ret = m_config->write(); - if (!ret) { - wxMessageDialog dialog(this, _("There was an error when writing the D-Star Repeater configuration file"), _("Error"), wxICON_ERROR); - dialog.ShowModal(); - } else { - wxMessageDialog dialog(this, _("The changes made will not take effect\nuntil the D-Star Repeater is (re)started"), _("Information"), wxICON_INFORMATION); - dialog.ShowModal(); - } -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigFrame.h b/DStarRepeaterConfig/DStarRepeaterConfigFrame.h deleted file mode 100644 index 207458f..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigFrame.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2011-2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigFrame_H -#define DStarRepeaterConfigFrame_H - -#include "DStarRepeaterConfigAnnouncementSet.h" -#include "DStarRepeaterConfigControllerSet.h" -#include "DStarRepeaterConfigCallsignSet.h" -#include "DStarRepeaterConfigControl1Set.h" -#include "DStarRepeaterConfigControl2Set.h" -#include "DStarRepeaterConfigNetworkSet.h" -#include "DStarRepeaterConfigBeaconSet.h" -#include "DStarRepeaterConfigModemSet.h" -#include "DStarRepeaterConfigTimesSet.h" -#include "DStarRepeaterConfigDefs.h" -#include "DStarRepeaterConfig.h" -#include "DStarDefines.h" - -#include - -class CDStarRepeaterConfigFrame : public wxFrame { -public: - CDStarRepeaterConfigFrame(const wxString& title, const wxString& confDir, const wxString& name); - virtual ~CDStarRepeaterConfigFrame(); - - virtual void onQuit(wxCommandEvent& event); - virtual void onSave(wxCommandEvent& event); - virtual void onAbout(wxCommandEvent& event); - virtual void onClose(wxCloseEvent& event); - -private: - CDStarRepeaterConfig* m_config; - CDStarRepeaterConfigCallsignSet* m_callsign; - CDStarRepeaterConfigNetworkSet* m_network; - CDStarRepeaterConfigTimesSet* m_times; - CDStarRepeaterConfigBeaconSet* m_beacon; - CDStarRepeaterConfigAnnouncementSet* m_announcement; - CDStarRepeaterConfigModemSet* m_modem; - CDStarRepeaterConfigControl1Set* m_control1; - CDStarRepeaterConfigControl2Set* m_control2; - CDStarRepeaterConfigControllerSet* m_controller; - - DECLARE_EVENT_TABLE() - - wxMenuBar* createMenuBar(); -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.cpp deleted file mode 100644 index 26f82bb..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2010,2011,2012,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigGMSKSet.h" - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH = 150U; - -CDStarRepeaterConfigGMSKSet::CDStarRepeaterConfigGMSKSet(wxWindow* parent, int id, USB_INTERFACE type, unsigned int address) : -wxDialog(parent, id, wxString(_("GMSK Modem Settings"))), -#if defined(WIN32) -m_type(NULL), -#endif -m_address(NULL) -{ - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - -#if defined(WIN32) - wxStaticText* typeLabel = new wxStaticText(this, -1, _("Type")); - sizer->Add(typeLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_type = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - m_type->Append(wxT("LibUSB")); - m_type->Append(wxT("WinUSB")); - sizer->Add(m_type, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_type->SetSelection(int(type)); -#endif - - wxStaticText* addressLabel = new wxStaticText(this, -1, _("Address")); - sizer->Add(addressLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_address = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); - m_address->Append(wxT("0x300")); - m_address->Append(wxT("0x301")); - m_address->Append(wxT("0x302")); - m_address->Append(wxT("0x303")); - m_address->Append(wxT("0x304")); - m_address->Append(wxT("0x305")); - m_address->Append(wxT("0x306")); - m_address->Append(wxT("0x307")); - m_address->Append(wxT("0x308")); - m_address->Append(wxT("0x309")); - m_address->Append(wxT("0x30A")); - m_address->Append(wxT("0x30B")); - m_address->Append(wxT("0x30C")); - m_address->Append(wxT("0x30D")); - m_address->Append(wxT("0x30E")); - m_address->Append(wxT("0x30F")); - m_address->Append(wxT("0x310")); - m_address->Append(wxT("0x311")); - m_address->Append(wxT("0x312")); - m_address->Append(wxT("0x313")); - m_address->Append(wxT("0x314")); - m_address->Append(wxT("0x315")); - m_address->Append(wxT("0x316")); - m_address->Append(wxT("0x317")); - m_address->Append(wxT("0x318")); - m_address->Append(wxT("0x319")); - m_address->Append(wxT("0x31A")); - m_address->Append(wxT("0x31B")); - m_address->Append(wxT("0x31C")); - m_address->Append(wxT("0x31D")); - m_address->Append(wxT("0x31E")); - m_address->Append(wxT("0x31F")); - sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_address->SetSelection(address - 0x0300U); - - topSizer->Add(sizer); - - topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - SetAutoLayout(true); - - topSizer->Fit(this); - topSizer->SetSizeHints(this); - - SetSizer(topSizer); -} - -CDStarRepeaterConfigGMSKSet::~CDStarRepeaterConfigGMSKSet() -{ -} - -bool CDStarRepeaterConfigGMSKSet::Validate() -{ -#if defined(WIN32) - if (m_type->GetCurrentSelection() == wxNOT_FOUND) - return false; -#endif - - return m_address->GetCurrentSelection() != wxNOT_FOUND; -} - -USB_INTERFACE CDStarRepeaterConfigGMSKSet::getType() const -{ -#if defined(WIN32) - int n = m_type->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return UI_WINUSB; - else - return USB_INTERFACE(n); -#else - return UI_LIBUSB; -#endif -} - -unsigned int CDStarRepeaterConfigGMSKSet::getAddress() const -{ - int n = m_address->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return 0x0300U; - else - return n + 0x0300U; -} - diff --git a/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.h b/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.h deleted file mode 100644 index ae2834b..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigGMSKSet.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2010,2011,2012 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigGMSKSet_H -#define DStarRepeaterConfigGMSKSet_H - -#include "DStarRepeaterConfigDefs.h" -#include "DStarDefines.h" - -#include - -class CDStarRepeaterConfigGMSKSet : public wxDialog { -public: - CDStarRepeaterConfigGMSKSet(wxWindow* parent, int id, USB_INTERFACE type, unsigned int address); - virtual ~CDStarRepeaterConfigGMSKSet(); - - virtual bool Validate(); - - virtual USB_INTERFACE getType() const; - virtual unsigned int getAddress() const; - -private: -#if defined(WIN32) - wxChoice* m_type; -#endif - wxChoice* m_address; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigIcomSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigIcomSet.cpp deleted file mode 100644 index a059713..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigIcomSet.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2011-2015,2018 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigIcomSet.h" -#include "SerialPortSelector.h" - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH1 = 150U; - -const unsigned int PORT_LENGTH = 5U; - - -CDStarRepeaterConfigIcomSet::CDStarRepeaterConfigIcomSet(wxWindow* parent, int id, const wxString& port) : -wxDialog(parent, id, wxString(_("Icom Settings"))), -m_port(NULL) -{ - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); - sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_port = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxArrayString ports = CSerialPortSelector::getDevices(); - for (unsigned int i = 0U; i < ports.GetCount(); i++) - m_port->Append(ports.Item(i)); - - bool found = m_port->SetStringSelection(port); - if (!found) - m_port->SetSelection(0); - - topSizer->Add(sizer); - - topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - SetAutoLayout(true); - - topSizer->Fit(this); - topSizer->SetSizeHints(this); - - SetSizer(topSizer); -} - -CDStarRepeaterConfigIcomSet::~CDStarRepeaterConfigIcomSet() -{ -} - -bool CDStarRepeaterConfigIcomSet::Validate() -{ - if (m_port->GetCurrentSelection() == wxNOT_FOUND) - return false; - - return true; -} - -wxString CDStarRepeaterConfigIcomSet::getPort() const -{ - int n = m_port->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return wxEmptyString; - - return m_port->GetStringSelection(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigIcomSet.h b/DStarRepeaterConfig/DStarRepeaterConfigIcomSet.h deleted file mode 100644 index 686c14f..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigIcomSet.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2011-2015,2018 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigIcomSet_H -#define DStarRepeaterConfigIcomSet_H - -#include "DStarRepeaterConfigDefs.h" -#include "DStarDefines.h" - -#include - -class CDStarRepeaterConfigIcomSet : public wxDialog { -public: - CDStarRepeaterConfigIcomSet(wxWindow* parent, int id, const wxString& port); - virtual ~CDStarRepeaterConfigIcomSet(); - - virtual bool Validate(); - - virtual wxString getPort() const; - -private: - wxChoice* m_port; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.cpp deleted file mode 100644 index 4d6ae30..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigMMDVMSet.h" -#include "SerialPortSelector.h" - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH1 = 150U; -const unsigned int CONTROL_WIDTH2 = 300U; - -const unsigned int PORT_LENGTH = 5U; - -CDStarRepeaterConfigMMDVMSet::CDStarRepeaterConfigMMDVMSet(wxWindow* parent, int id, const wxString& port, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel) : -wxDialog(parent, id, wxString(_("MMDVM Settings"))), -m_port(NULL), -m_txInvert(NULL), -m_rxInvert(NULL), -m_pttInvert(NULL), -m_txDelay(NULL), -m_rxLevel(NULL), -m_txLevel(NULL) -{ - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); - sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_port = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxArrayString ports = CSerialPortSelector::getDevices(); - for (unsigned int i = 0U; i < ports.GetCount(); i++) - m_port->Append(ports.Item(i)); - - bool found = m_port->SetStringSelection(port); - if (!found) - m_port->SetSelection(0); - - wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); - sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_txInvert->Append(_("Off")); - m_txInvert->Append(_("On")); - sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_txInvert->SetSelection(txInvert ? 1 : 0); - - wxStaticText* rxInvertLabel = new wxStaticText(this, -1, _("RX Inversion")); - sizer->Add(rxInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_rxInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_rxInvert->Append(_("Off")); - m_rxInvert->Append(_("On")); - sizer->Add(m_rxInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_rxInvert->SetSelection(rxInvert ? 1 : 0); - - wxStaticText* pttInvertLabel = new wxStaticText(this, -1, _("PTT Inversion")); - sizer->Add(pttInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_pttInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_pttInvert->Append(_("Off")); - m_pttInvert->Append(_("On")); - sizer->Add(m_pttInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_pttInvert->SetSelection(pttInvert ? 1 : 0); - - wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); - sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txDelay = new wxSlider(this, -1, txDelay, 0, 500, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* rxLevelLabel = new wxStaticText(this, -1, _("RX Level (%)")); - sizer->Add(rxLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_rxLevel = new wxSlider(this, -1, rxLevel, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_rxLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* txLevelLabel = new wxStaticText(this, -1, _("TX Level (%)")); - sizer->Add(txLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txLevel = new wxSlider(this, -1, txLevel, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_txLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - topSizer->Add(sizer); - - topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - SetAutoLayout(true); - - topSizer->Fit(this); - topSizer->SetSizeHints(this); - - SetSizer(topSizer); -} - -CDStarRepeaterConfigMMDVMSet::~CDStarRepeaterConfigMMDVMSet() -{ -} - -bool CDStarRepeaterConfigMMDVMSet::Validate() -{ - if (m_port->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_txInvert->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_rxInvert->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_pttInvert->GetCurrentSelection() == wxNOT_FOUND) - return false; - - return true; -} - -wxString CDStarRepeaterConfigMMDVMSet::getPort() const -{ - int n = m_port->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return wxEmptyString; - - return m_port->GetStringSelection(); -} - -bool CDStarRepeaterConfigMMDVMSet::getRXInvert() const -{ - int n = m_rxInvert->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -bool CDStarRepeaterConfigMMDVMSet::getTXInvert() const -{ - int n = m_txInvert->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -bool CDStarRepeaterConfigMMDVMSet::getPTTInvert() const -{ - int n = m_pttInvert->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -unsigned int CDStarRepeaterConfigMMDVMSet::getTXDelay() const -{ - return (unsigned int)m_txDelay->GetValue(); -} - -unsigned int CDStarRepeaterConfigMMDVMSet::getRXLevel() const -{ - return (unsigned int)m_rxLevel->GetValue(); -} - -unsigned int CDStarRepeaterConfigMMDVMSet::getTXLevel() const -{ - return (unsigned int)m_txLevel->GetValue(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.h b/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.h deleted file mode 100644 index 9b5c288..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigMMDVMSet.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigMMDVMSet_H -#define DStarRepeaterConfigMMDVMSet_H - -#include "DStarRepeaterConfigDefs.h" -#include "DStarDefines.h" - -#include - -class CDStarRepeaterConfigMMDVMSet : public wxDialog { -public: - CDStarRepeaterConfigMMDVMSet(wxWindow* parent, int id, const wxString& port, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel); - virtual ~CDStarRepeaterConfigMMDVMSet(); - - virtual bool Validate(); - - virtual wxString getPort() const; - virtual bool getRXInvert() const; - virtual bool getTXInvert() const; - virtual bool getPTTInvert() const; - virtual unsigned int getTXDelay() const; - virtual unsigned int getRXLevel() const; - virtual unsigned int getTXLevel() const; - -private: - wxChoice* m_port; - wxChoice* m_txInvert; - wxChoice* m_rxInvert; - wxChoice* m_pttInvert; - wxSlider* m_txDelay; - wxSlider* m_rxLevel; - wxSlider* m_txLevel; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigModemSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigModemSet.cpp deleted file mode 100644 index abb6b77..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigModemSet.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (C) 2011-2015,2018 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigSoundCardSet.h" -#include "DStarRepeaterConfigDVRPTR1Set.h" -#include "DStarRepeaterConfigDVRPTR2Set.h" -#include "DStarRepeaterConfigDVRPTR3Set.h" -#include "DStarRepeaterConfigDVMegaSet.h" -#include "DStarRepeaterConfigModemSet.h" -#include "DStarRepeaterConfigMMDVMSet.h" -#include "DStarRepeaterConfigGMSKSet.h" -#include "DStarRepeaterConfigDVAPSet.h" -#include "DStarRepeaterConfigIcomSet.h" -#include "DStarRepeaterConfigSplitSet.h" - -enum { - Button_Configure = 7088 -}; - -BEGIN_EVENT_TABLE(CDStarRepeaterConfigModemSet, wxPanel) - EVT_BUTTON(Button_Configure, CDStarRepeaterConfigModemSet::onConfigure) -END_EVENT_TABLE() - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH1 = 210U; -const unsigned int CONTROL_WIDTH2 = 130U; - -CDStarRepeaterConfigModemSet::CDStarRepeaterConfigModemSet(wxWindow* parent, int id, const wxString& title, CDStarRepeaterConfig* config, const wxString& type) : -wxPanel(parent, id), -m_config(config), -m_title(title), -m_type(NULL) -{ - wxASSERT(config != NULL); - - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* typeLabel = new wxStaticText(this, -1, _("Type")); - sizer->Add(typeLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_type = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_type->Append(wxT("DVAP")); - m_type->Append(wxT("DVMEGA")); - m_type->Append(wxT("DV-RPTR V1")); - m_type->Append(wxT("DV-RPTR V2")); - m_type->Append(wxT("DV-RPTR V3")); - m_type->Append(wxT("GMSK Modem")); - m_type->Append(wxT("MMDVM")); - m_type->Append(wxT("Sound Card")); - m_type->Append(wxT("Split")); - m_type->Append(wxT("Icom Access Point/Terminal Mode")); - sizer->Add(m_type, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* dummy = new wxStaticText(this, -1, wxEmptyString); - sizer->Add(dummy, 0, wxALL, BORDER_SIZE); - - wxButton* configure = new wxButton(this, Button_Configure, _("Configure..."), wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); - sizer->Add(configure, 0, wxALL, BORDER_SIZE); - - if (type.IsEmpty()) { - m_type->SetSelection(0); - } else { - int n = m_type->SetStringSelection(type); - if (n == wxNOT_FOUND) - m_type->SetSelection(0); - } - - SetAutoLayout(true); - - sizer->Fit(this); - sizer->SetSizeHints(this); - - SetSizer(sizer); -} - -CDStarRepeaterConfigModemSet::~CDStarRepeaterConfigModemSet() -{ -} - -bool CDStarRepeaterConfigModemSet::Validate() -{ - return m_type->GetCurrentSelection() != wxNOT_FOUND; -} - -wxString CDStarRepeaterConfigModemSet::getType() const -{ - int n = m_type->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return wxT("DVAP"); - - return m_type->GetStringSelection(); -} - -void CDStarRepeaterConfigModemSet::onConfigure(wxCommandEvent& event) -{ - int n = m_type->GetSelection(); - if (n == wxNOT_FOUND) - return; - - wxString type = m_type->GetStringSelection(); - - if (type.IsSameAs(wxT("DVAP"))) { - wxString port; - unsigned int frequency; - int power, squelch; - m_config->getDVAP(port, frequency, power, squelch); - CDStarRepeaterConfigDVAPSet modem(this, -1, port, frequency, power, squelch); - if (modem.ShowModal() == wxID_OK) { - if (modem.Validate()) { - port = modem.getPort(); - frequency = modem.getFrequency(); - power = modem.getPower(); - squelch = modem.getSquelch(); - m_config->setDVAP(port, frequency, power, squelch); - } - } - } else if (type.IsSameAs(wxT("GMSK Modem"))) { - USB_INTERFACE modemType; - unsigned int modemAddress; - m_config->getGMSK(modemType, modemAddress); - CDStarRepeaterConfigGMSKSet modem(this, -1, modemType, modemAddress); - if (modem.ShowModal() == wxID_OK) { - if (modem.Validate()) { - modemType = modem.getType(); - modemAddress = modem.getAddress(); - m_config->setGMSK(modemType, modemAddress); - } - } - } else if (type.IsSameAs(wxT("DV-RPTR V1"))) { - wxString port; - bool txInvert, rxInvert, channel; - unsigned int modLevel, txDelay; - m_config->getDVRPTR1(port, rxInvert, txInvert, channel, modLevel, txDelay); - CDStarRepeaterConfigDVRPTR1Set modem(this, -1, port, rxInvert, txInvert, channel, modLevel, txDelay); - if (modem.ShowModal() == wxID_OK) { - if (modem.Validate()) { - port = modem.getPort(); - rxInvert = modem.getRXInvert(); - txInvert = modem.getTXInvert(); - channel = modem.getChannel(); - modLevel = modem.getModLevel(); - txDelay = modem.getTXDelay(); - m_config->setDVRPTR1(port, rxInvert, txInvert, channel, modLevel, txDelay); - } - } - } else if (type.IsSameAs(wxT("DV-RPTR V2"))) { - CONNECTION_TYPE connType; - wxString usbPort, address; - bool txInvert; - unsigned int port, modLevel, txDelay; - m_config->getDVRPTR2(connType, usbPort, address, port, txInvert, modLevel, txDelay); - CDStarRepeaterConfigDVRPTR2Set modem(this, -1, connType, usbPort, address, port, txInvert, modLevel, txDelay); - if (modem.ShowModal() == wxID_OK) { - if (modem.Validate()) { - connType = modem.getConnectionType(); - usbPort = modem.getUSBPort(); - address = modem.getAddress(); - port = modem.getPort(); - txInvert = modem.getTXInvert(); - modLevel = modem.getModLevel(); - txDelay = modem.getTXDelay(); - m_config->setDVRPTR2(connType, usbPort, address, port, txInvert, modLevel, txDelay); - } - } - } else if (type.IsSameAs(wxT("DV-RPTR V3"))) { - CONNECTION_TYPE connType; - wxString usbPort, address; - bool txInvert; - unsigned int port, modLevel, txDelay; - m_config->getDVRPTR3(connType, usbPort, address, port, txInvert, modLevel, txDelay); - CDStarRepeaterConfigDVRPTR3Set modem(this, -1, connType, usbPort, address, port, txInvert, modLevel, txDelay); - if (modem.ShowModal() == wxID_OK) { - if (modem.Validate()) { - connType = modem.getConnectionType(); - usbPort = modem.getUSBPort(); - address = modem.getAddress(); - port = modem.getPort(); - txInvert = modem.getTXInvert(); - modLevel = modem.getModLevel(); - txDelay = modem.getTXDelay(); - m_config->setDVRPTR3(connType, usbPort, address, port, txInvert, modLevel, txDelay); - } - } - } else if (type.IsSameAs(wxT("DVMEGA"))) { - wxString port; - DVMEGA_VARIANT variant; - bool txInvert, rxInvert; - unsigned int txDelay, rxFrequency, txFrequency, power; - m_config->getDVMEGA(port, variant, rxInvert, txInvert, txDelay, rxFrequency, txFrequency, power); - CDStarRepeaterConfigDVMegaSet modem(this, -1, port, variant, rxInvert, txInvert, txDelay, rxFrequency, txFrequency, power); - if (modem.ShowModal() == wxID_OK) { - if (modem.Validate()) { - port = modem.getPort(); - variant = modem.getVariant(); - rxInvert = modem.getRXInvert(); - txInvert = modem.getTXInvert(); - txDelay = modem.getTXDelay(); - rxFrequency = modem.getRXFrequency(); - txFrequency = modem.getTXFrequency(); - power = modem.getPower(); - m_config->setDVMEGA(port, variant, rxInvert, txInvert, txDelay, rxFrequency, txFrequency, power); - } - } - } else if (type.IsSameAs(wxT("MMDVM"))) { - wxString port; - bool txInvert, rxInvert, pttInvert; - unsigned int txDelay, rxLevel, txLevel; - m_config->getMMDVM(port, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); - CDStarRepeaterConfigMMDVMSet modem(this, -1, port, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); - if (modem.ShowModal() == wxID_OK) { - if (modem.Validate()) { - port = modem.getPort(); - rxInvert = modem.getRXInvert(); - txInvert = modem.getTXInvert(); - pttInvert = modem.getPTTInvert(); - txDelay = modem.getTXDelay(); - rxLevel = modem.getRXLevel(); - txLevel = modem.getTXLevel(); - m_config->setMMDVM(port, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel); - } - } - } else if (type.IsSameAs(wxT("Sound Card"))) { - wxString rxDevice, txDevice; - bool txInvert, rxInvert; - wxFloat32 rxLevel, txLevel; - unsigned int txDelay, txTail; - m_config->getSoundCard(rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); - CDStarRepeaterConfigSoundCardSet modem(this, -1, rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); - if (modem.ShowModal() == wxID_OK) { - if (modem.Validate()) { - rxDevice = modem.getRXDevice(); - txDevice = modem.getTXDevice(); - rxInvert = modem.getRXInvert(); - txInvert = modem.getTXInvert(); - rxLevel = modem.getRXLevel(); - txLevel = modem.getTXLevel(); - txDelay = modem.getTXDelay(); - txTail = modem.getTXTail(); - m_config->setSoundCard(rxDevice, txDevice, rxInvert, txInvert, rxLevel, txLevel, txDelay, txTail); - } - } - } else if (type.IsSameAs(wxT("Split"))) { - wxString localAddress; - unsigned int localPort, timeout; - wxArrayString transmitterNames, receiverNames; - m_config->getSplit(localAddress, localPort, transmitterNames, receiverNames, timeout); - CDStarRepeaterConfigSplitSet modem(this, -1, localAddress, localPort, transmitterNames, receiverNames, timeout); - if (modem.ShowModal() == wxID_OK) { - if (modem.Validate()) { - localAddress = modem.getLocalAddress(); - localPort = modem.getLocalPort(); - transmitterNames = modem.getTransmitterNames(); - receiverNames = modem.getReceiverNames(); - timeout = modem.getTimeout(); - m_config->setSplit(localAddress, localPort, transmitterNames, receiverNames, timeout); - } - } - } else if (type.IsSameAs(wxT("Icom Access Point/Terminal Mode"))) { - wxString port; - m_config->getIcom(port); - CDStarRepeaterConfigIcomSet modem(this, -1, port); - if (modem.ShowModal() == wxID_OK) { - if (modem.Validate()) { - port = modem.getPort(); - m_config->setIcom(port); - } - } - } -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigModemSet.h b/DStarRepeaterConfig/DStarRepeaterConfigModemSet.h deleted file mode 100644 index 9a68ef4..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigModemSet.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigModemSet_H -#define DStarRepeaterConfigModemSet_H - -#include "DStarRepeaterConfigDefs.h" -#include "DStarRepeaterConfig.h" -#include "DStarDefines.h" - -#include - -class CDStarRepeaterConfigModemSet : public wxPanel { -public: - CDStarRepeaterConfigModemSet(wxWindow* parent, int id, const wxString& title, CDStarRepeaterConfig* config, const wxString& type); - virtual ~CDStarRepeaterConfigModemSet(); - - virtual bool Validate(); - - virtual wxString getType() const; - - virtual void onConfigure(wxCommandEvent& event); - -private: - CDStarRepeaterConfig* m_config; - wxString m_title; - wxChoice* m_type; - - DECLARE_EVENT_TABLE() -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.cpp deleted file mode 100644 index 8b71527..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2010,2011,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigNetworkSet.h" - -const unsigned int ADDRESS_WIDTH = 120U; -const unsigned int PORT_WIDTH = 80U; - -const unsigned int ADDRESS_LENGTH = 15U; -const unsigned int PORT_LENGTH = 5U; - -const unsigned int BORDER_SIZE = 5U; - -CDStarRepeaterConfigNetworkSet::CDStarRepeaterConfigNetworkSet(wxWindow* parent, int id, const wxString& title, const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name) : -wxPanel(parent, id), -m_title(title), -m_gatewayAddress(NULL), -m_gatewayPort(NULL), -m_localAddress(NULL), -m_localPort(NULL), -m_name(NULL) -{ - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* gatewayAddressLabel = new wxStaticText(this, -1, _("Gateway Address")); - sizer->Add(gatewayAddressLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_gatewayAddress = new CAddressTextCtrl(this, -1, gatewayAddress, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); - m_gatewayAddress->SetMaxLength(ADDRESS_LENGTH); - sizer->Add(m_gatewayAddress, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* gatewayPortLabel = new wxStaticText(this, -1, _("Gateway Port")); - sizer->Add(gatewayPortLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - wxString buffer; - buffer.Printf(wxT("%u"), gatewayPort); - - m_gatewayPort = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); - m_gatewayPort->SetMaxLength(PORT_LENGTH); - sizer->Add(m_gatewayPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* localAddressLabel = new wxStaticText(this, -1, _("Local Address")); - sizer->Add(localAddressLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_localAddress = new CAddressTextCtrl(this, -1, localAddress, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); - m_localAddress->SetMaxLength(ADDRESS_LENGTH); - sizer->Add(m_localAddress, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* localPortLabel = new wxStaticText(this, -1, _("Local Port")); - sizer->Add(localPortLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - buffer.Printf(wxT("%u"), localPort); - - m_localPort = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); - m_localPort->SetMaxLength(PORT_LENGTH); - sizer->Add(m_localPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* nameLabel = new wxStaticText(this, -1, _("Name")); - sizer->Add(nameLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_name = new wxTextCtrl(this, -1, name, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); - sizer->Add(m_name, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - SetAutoLayout(true); - - sizer->Fit(this); - sizer->SetSizeHints(this); - - SetSizer(sizer); -} - - -CDStarRepeaterConfigNetworkSet::~CDStarRepeaterConfigNetworkSet() -{ -} - -bool CDStarRepeaterConfigNetworkSet::Validate() -{ - unsigned int port = getGatewayPort(); - - if (port == 0U || port > 65535U) { - wxMessageDialog dialog(this, _("The Gateway Port is not valid"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - port = getLocalPort(); - - if (port == 0U || port > 65535U) { - wxMessageDialog dialog(this, _("The Local Port is not valid"), m_title + _(" Error"), wxICON_ERROR); - dialog.ShowModal(); - return false; - } - - return true; -} - -wxString CDStarRepeaterConfigNetworkSet::getGatewayAddress() const -{ - return m_gatewayAddress->GetValue(); -} - -unsigned int CDStarRepeaterConfigNetworkSet::getGatewayPort() const -{ - unsigned long n; - - m_gatewayPort->GetValue().ToULong(&n); - - return n; -} - -wxString CDStarRepeaterConfigNetworkSet::getLocalAddress() const -{ - return m_localAddress->GetValue(); -} - -unsigned int CDStarRepeaterConfigNetworkSet::getLocalPort() const -{ - unsigned long n; - - m_localPort->GetValue().ToULong(&n); - - return n; -} - -wxString CDStarRepeaterConfigNetworkSet::getName() const -{ - return m_name->GetValue(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.h b/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.h deleted file mode 100644 index e3052b1..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigNetworkSet.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2010,2011,2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigNetworkSet_H -#define DStarRepeaterConfigNetworkSet_H - -#include "AddressTextCtrl.h" -#include "PortTextCtrl.h" - -#include - -class CDStarRepeaterConfigNetworkSet : public wxPanel { -public: - CDStarRepeaterConfigNetworkSet(wxWindow* parent, int id, const wxString& title, const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name); - virtual ~CDStarRepeaterConfigNetworkSet(); - - virtual bool Validate(); - - virtual wxString getGatewayAddress() const; - virtual unsigned int getGatewayPort() const; - virtual wxString getLocalAddress() const; - virtual unsigned int getLocalPort() const; - virtual wxString getName() const; - -private: - wxString m_title; - CAddressTextCtrl* m_gatewayAddress; - CPortTextCtrl* m_gatewayPort; - CAddressTextCtrl* m_localAddress; - CPortTextCtrl* m_localPort; - wxTextCtrl* m_name; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.cpp deleted file mode 100644 index 98e1c9a..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2009-2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigSoundCardSet.h" -#include "SoundCardReaderWriter.h" - -const unsigned int BORDER_SIZE = 5U; -const unsigned int CONTROL_WIDTH1 = 150U; -const unsigned int CONTROL_WIDTH2 = 300U; -const unsigned int CONTROL_WIDTH3 = 350U; - -const unsigned int ADDRESS_LENGTH = 15U; -const unsigned int PORT_LENGTH = 5U; - - -CDStarRepeaterConfigSoundCardSet::CDStarRepeaterConfigSoundCardSet(wxWindow* parent, int id, const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail) : -wxDialog(parent, id, wxString(_("Sound Card Settings"))), -m_rxDevice(NULL), -m_txDevice(NULL), -m_rxInvert(NULL), -m_txInvert(NULL), -m_rxLevel(NULL), -m_txLevel(NULL), -m_txDelay(NULL), -m_txTail(NULL) -{ - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* rxDeviceLabel = new wxStaticText(this, -1, _("RX Device")); - sizer->Add(rxDeviceLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_rxDevice = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH3, -1)); - sizer->Add(m_rxDevice, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxArrayString devs = CSoundCardReaderWriter::getReadDevices(); - for (unsigned int i = 0U; i < devs.GetCount(); i++) - m_rxDevice->Append(devs.Item(i)); - - bool found = m_rxDevice->SetStringSelection(rxDevice); - if (!found) - m_rxDevice->SetSelection(0); - - wxStaticText* txDeviceLabel = new wxStaticText(this, -1, _("TX Device")); - sizer->Add(txDeviceLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txDevice = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH3, -1)); - sizer->Add(m_txDevice, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - devs = CSoundCardReaderWriter::getWriteDevices(); - for (unsigned int i = 0U; i < devs.GetCount(); i++) - m_txDevice->Append(devs.Item(i)); - - found = m_txDevice->SetStringSelection(txDevice); - if (!found) - m_txDevice->SetSelection(0); - - wxStaticText* rxInvertLabel = new wxStaticText(this, -1, _("RX Inversion")); - sizer->Add(rxInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_rxInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_rxInvert->Append(_("Off")); - m_rxInvert->Append(_("On")); - sizer->Add(m_rxInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_rxInvert->SetSelection(rxInvert ? 1 : 0); - - wxStaticText* txInvertLabel = new wxStaticText(this, -1, _("TX Inversion")); - sizer->Add(txInvertLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txInvert = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); - m_txInvert->Append(_("Off")); - m_txInvert->Append(_("On")); - sizer->Add(m_txInvert, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - m_txInvert->SetSelection(txInvert ? 1 : 0); - - wxStaticText* rxLevelLabel = new wxStaticText(this, -1, _("RX Level (%)")); - sizer->Add(rxLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - int level = int(rxLevel * 100.0F + 0.5F); - m_rxLevel = new wxSlider(this, -1, level, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_rxLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* txLevelLabel = new wxStaticText(this, -1, _("TX Level (%)")); - sizer->Add(txLevelLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - level = int(txLevel * 100.0F + 0.5F); - m_txLevel = new wxSlider(this, -1, level, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_txLevel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* txDelayLabel = new wxStaticText(this, -1, _("TX Delay (ms)")); - sizer->Add(txDelayLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txDelay = new wxSlider(this, -1, txDelay, 0, 500, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_txDelay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* txTailLabel = new wxStaticText(this, -1, _("TX Tail (ms)")); - sizer->Add(txTailLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_txTail = new wxSlider(this, -1, txTail, 0, 100, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_txTail, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - topSizer->Add(sizer); - - topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - SetAutoLayout(true); - - topSizer->Fit(this); - topSizer->SetSizeHints(this); - - SetSizer(topSizer); -} - -CDStarRepeaterConfigSoundCardSet::~CDStarRepeaterConfigSoundCardSet() -{ -} - -bool CDStarRepeaterConfigSoundCardSet::Validate() -{ - if (m_rxDevice->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_txDevice->GetCurrentSelection() == wxNOT_FOUND) - return false; - - if (m_rxInvert->GetCurrentSelection() == wxNOT_FOUND) - return false; - - return m_txInvert->GetCurrentSelection() != wxNOT_FOUND; -} - -wxString CDStarRepeaterConfigSoundCardSet::getRXDevice() const -{ - int n = m_rxDevice->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return wxEmptyString; - - return m_rxDevice->GetStringSelection(); -} - -wxString CDStarRepeaterConfigSoundCardSet::getTXDevice() const -{ - int n = m_txDevice->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return wxEmptyString; - - return m_txDevice->GetStringSelection(); -} - -bool CDStarRepeaterConfigSoundCardSet::getRXInvert() const -{ - int n = m_rxInvert->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -bool CDStarRepeaterConfigSoundCardSet::getTXInvert() const -{ - int n = m_txInvert->GetCurrentSelection(); - - if (n == wxNOT_FOUND) - return false; - - return n == 1; -} - -wxFloat32 CDStarRepeaterConfigSoundCardSet::getRXLevel() const -{ - return wxFloat32(m_rxLevel->GetValue()) / 100.0F; -} - -wxFloat32 CDStarRepeaterConfigSoundCardSet::getTXLevel() const -{ - return wxFloat32(m_txLevel->GetValue()) / 100.0F; -} - -unsigned int CDStarRepeaterConfigSoundCardSet::getTXDelay() const -{ - return (unsigned int)m_txDelay->GetValue(); -} - -unsigned int CDStarRepeaterConfigSoundCardSet::getTXTail() const -{ - return (unsigned int)m_txTail->GetValue(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.h b/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.h deleted file mode 100644 index 82e32b0..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigSoundCardSet.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2009-2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigSoundCardSet_H -#define DStarRepeaterConfigSoundCardSet_H - -#include "DStarRepeaterConfigDefs.h" -#include "DStarDefines.h" - -#include - -class CDStarRepeaterConfigSoundCardSet : public wxDialog { -public: - CDStarRepeaterConfigSoundCardSet(wxWindow* parent, int id, const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail); - virtual ~CDStarRepeaterConfigSoundCardSet(); - - virtual bool Validate(); - - virtual wxString getRXDevice() const; - virtual wxString getTXDevice() const; - virtual bool getRXInvert() const; - virtual bool getTXInvert() const; - virtual wxFloat32 getRXLevel() const; - virtual wxFloat32 getTXLevel() const; - virtual unsigned int getTXDelay() const; - virtual unsigned int getTXTail() const; - -private: - wxChoice* m_rxDevice; - wxChoice* m_txDevice; - wxChoice* m_rxInvert; - wxChoice* m_txInvert; - wxSlider* m_rxLevel; - wxSlider* m_txLevel; - wxSlider* m_txDelay; - wxSlider* m_txTail; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.cpp deleted file mode 100644 index 5f2f500..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigSplitSet.h" -#include "DStarDefines.h" - -const unsigned int BORDER_SIZE = 5U; - -const unsigned int ADDRESS_WIDTH = 120U; -const unsigned int PORT_WIDTH = 80U; - -const unsigned int ADDRESS_LENGTH = 15U; -const unsigned int PORT_LENGTH = 5U; - -const unsigned int SLIDER_WIDTH = 200U; - -CDStarRepeaterConfigSplitSet::CDStarRepeaterConfigSplitSet(wxWindow* parent, int id, const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout) : -wxDialog(parent, id, wxString(_("Split Settings"))), -m_localAddress(NULL), -m_localPort(NULL), -m_transmitterNames(NULL), -m_receiverNames(NULL), -m_timeout(NULL), -m_txCount(0U), -m_rxCount(0U) -{ - m_txCount = transmitterNames.GetCount(); - m_rxCount = receiverNames.GetCount(); - - if (m_txCount > SPLIT_TX_GUI_COUNT) - m_txCount = SPLIT_TX_GUI_COUNT; - - if (m_rxCount > SPLIT_RX_GUI_COUNT) - m_rxCount = SPLIT_RX_GUI_COUNT; - - m_transmitterNames = new wxTextCtrl*[m_txCount]; - m_receiverNames = new wxTextCtrl*[m_rxCount]; - - wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* localLabel = new wxStaticText(this, -1, _("Local Addr/Port")); - sizer->Add(localLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxBoxSizer* localSizer = new wxBoxSizer(wxHORIZONTAL); - - m_localAddress = new CAddressTextCtrl(this, -1, localAddress, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); - m_localAddress->SetMaxLength(ADDRESS_LENGTH); - localSizer->Add(m_localAddress, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxString buffer; - buffer.Printf(wxT("%u"), localPort); - - m_localPort = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); - m_localPort->SetMaxLength(PORT_LENGTH); - localSizer->Add(m_localPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - sizer->Add(localSizer); - - for (unsigned int i = 0U; i < m_txCount; i++) { - wxString text; - text.Printf(_("TX %u Name"), i + 1U); - - wxStaticText* transmitterLabel = new wxStaticText(this, -1, text); - sizer->Add(transmitterLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_transmitterNames[i] = new wxTextCtrl(this, -1, transmitterNames.Item(i), wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); - sizer->Add(m_transmitterNames[i], 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - } - - for (unsigned int i = 0U; i < m_rxCount; i++) { - wxString text; - text.Printf(_("RX %u Name"), i + 1U); - - wxStaticText* receiverLabel = new wxStaticText(this, -1, text); - sizer->Add(receiverLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_receiverNames[i] = new wxTextCtrl(this, -1, receiverNames.Item(i), wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); - sizer->Add(m_receiverNames[i], 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - } - - wxStaticText* timeoutLabel = new wxStaticText(this, -1, _("Packet timeout (ms)")); - sizer->Add(timeoutLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - m_timeout = new wxSlider(this, -1, timeout, 10, 300, wxDefaultPosition, wxSize(SLIDER_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_timeout, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - topSizer->Add(sizer); - - topSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - SetAutoLayout(true); - - topSizer->Fit(this); - topSizer->SetSizeHints(this); - - SetSizer(topSizer); -} - -CDStarRepeaterConfigSplitSet::~CDStarRepeaterConfigSplitSet() -{ -} - -bool CDStarRepeaterConfigSplitSet::Validate() -{ - return true; -} - -wxString CDStarRepeaterConfigSplitSet::getLocalAddress() const -{ - return m_localAddress->GetValue(); -} - -unsigned int CDStarRepeaterConfigSplitSet::getLocalPort() const -{ - unsigned long n; - - m_localPort->GetValue().ToULong(&n); - - return n; -} - -wxArrayString CDStarRepeaterConfigSplitSet::getTransmitterNames() const -{ - wxArrayString names; - - for (unsigned int i = 0U; i < m_txCount; i++) - names.Add(m_transmitterNames[i]->GetValue()); - - return names; -} - -wxArrayString CDStarRepeaterConfigSplitSet::getReceiverNames() const -{ - wxArrayString names; - - for (unsigned int i = 0U; i < m_rxCount; i++) - names.Add(m_receiverNames[i]->GetValue()); - - return names; -} - -unsigned int CDStarRepeaterConfigSplitSet::getTimeout() const -{ - return (unsigned int)m_timeout->GetValue(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.h b/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.h deleted file mode 100644 index 16122d2..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigSplitSet.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2014 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigSplitSet_H -#define DStarRepeaterConfigSplitSet_H - -#include "DStarRepeaterConfigDefs.h" -#include "AddressTextCtrl.h" -#include "PortTextCtrl.h" - -#include - -class CDStarRepeaterConfigSplitSet : public wxDialog { -public: - CDStarRepeaterConfigSplitSet(wxWindow* parent, int id, const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout); - virtual ~CDStarRepeaterConfigSplitSet(); - - virtual bool Validate(); - - virtual wxString getLocalAddress() const; - virtual unsigned int getLocalPort() const; - - virtual wxArrayString getTransmitterNames() const; - - virtual wxArrayString getReceiverNames() const; - - virtual unsigned int getTimeout() const; - -private: - CAddressTextCtrl* m_localAddress; - CPortTextCtrl* m_localPort; - wxTextCtrl** m_transmitterNames; - wxTextCtrl** m_receiverNames; - wxSlider* m_timeout; - unsigned int m_txCount; - unsigned int m_rxCount; -}; - -#endif diff --git a/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.cpp b/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.cpp deleted file mode 100644 index 29598d8..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2011,2012 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DStarRepeaterConfigTimesSet.h" - -const unsigned int TIMES_WIDTH = 300U; - -const unsigned int BORDER_SIZE = 5U; - -CDStarRepeaterConfigTimesSet::CDStarRepeaterConfigTimesSet(wxWindow* parent, int id, const wxString& title, unsigned int timeout, unsigned int ackTime) : -wxPanel(parent, id), -m_title(title), -m_timeout(NULL), -m_ackTime(NULL) -{ - wxFlexGridSizer* sizer = new wxFlexGridSizer(2); - - wxStaticText* timeoutLabel = new wxStaticText(this, -1, _("Timeout (secs)")); - sizer->Add(timeoutLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - m_timeout = new wxSlider(this, -1, timeout, 0, 300, wxDefaultPosition, wxSize(TIMES_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_timeout, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - wxStaticText* ackTimeLabel = new wxStaticText(this, -1, _("Ack Time (ms)")); - sizer->Add(ackTimeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); - - if (ackTime < 100U) - ackTime = 500U; - - m_ackTime = new wxSlider(this, -1, ackTime, 100, 2000, wxDefaultPosition, wxSize(TIMES_WIDTH, -1), wxSL_HORIZONTAL | wxSL_LABELS); - sizer->Add(m_ackTime, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); - - SetAutoLayout(true); - - SetSizer(sizer); -} - - -CDStarRepeaterConfigTimesSet::~CDStarRepeaterConfigTimesSet() -{ -} - -bool CDStarRepeaterConfigTimesSet::Validate() -{ - return true; -} - -unsigned int CDStarRepeaterConfigTimesSet::getTimeout() const -{ - return m_timeout->GetValue(); -} - -unsigned int CDStarRepeaterConfigTimesSet::getAckTime() const -{ - return m_ackTime->GetValue(); -} diff --git a/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.h b/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.h deleted file mode 100644 index 9d6eef4..0000000 --- a/DStarRepeaterConfig/DStarRepeaterConfigTimesSet.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2011,2012 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef DStarRepeaterConfigTimesSet_H -#define DStarRepeaterConfigTimesSet_H - -#include - -class CDStarRepeaterConfigTimesSet : public wxPanel { -public: - CDStarRepeaterConfigTimesSet(wxWindow* parent, int id, const wxString& title, unsigned int timeout, unsigned int ackTime); - virtual ~CDStarRepeaterConfigTimesSet(); - - virtual bool Validate(); - - virtual unsigned int getTimeout() const; - virtual unsigned int getAckTime() const; - -private: - wxString m_title; - wxSlider* m_timeout; - wxSlider* m_ackTime; -}; - -#endif diff --git a/DStarRepeaterConfig/Makefile b/DStarRepeaterConfig/Makefile deleted file mode 100644 index 7b1d453..0000000 --- a/DStarRepeaterConfig/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -OBJECTS = DStarRepeaterConfigActiveHangSet.o DStarRepeaterConfigAnnouncementSet.o DStarRepeaterConfigApp.o DStarRepeaterConfigBeaconSet.o \ - DStarRepeaterConfigCallsignSet.o DStarRepeaterConfigControl1Set.o DStarRepeaterConfigControl2Set.o \ - DStarRepeaterConfigControllerSet.o DStarRepeaterConfigDVAPSet.o DStarRepeaterConfigDVMegaSet.o DStarRepeaterConfigDVRPTR1Set.o \ - DStarRepeaterConfigDVRPTR2Set.o DStarRepeaterConfigDVRPTR3Set.o DStarRepeaterConfigFrame.o DStarRepeaterConfigGMSKSet.o \ - DStarRepeaterConfigIcomSet.o DStarRepeaterConfigMMDVMSet.o DStarRepeaterConfigModemSet.o DStarRepeaterConfigNetworkSet.o \ - DStarRepeaterConfigSoundCardSet.o DStarRepeaterConfigSplitSet.o DStarRepeaterConfigTimesSet.o - -.PHONY: all install clean -all: dstarrepeaterconfig - -dstarrepeaterconfig: $(OBJECTS) ../GUICommon/GUICommon.a ../Common/Common.a - $(CXX) $(OBJECTS) ../GUICommon/GUICommon.a ../Common/Common.a $(LDFLAGS) $(GUILIBS) -o dstarrepeaterconfig - --include $(OBJECTS:.o=.d) - -%.o: %.cpp - $(CXX) $(CFLAGS) -I../GUICommon -I../Common -c -o $@ $< - $(CXX) -MM $(CFLAGS) -I../GUICommon -I../Common $< > $*.d - -install: - install -g root -o root -m 0755 dstarrepeaterconfig $(DESTDIR)$(BINDIR) - -clean: - $(RM) dstarrepeaterconfig *.o *.d *.bak *~ - -../GUICommon/GUICommon.a: -../Common/Common.a: diff --git a/Data/dstarrepeater.ini.example b/Data/dstarrepeater.ini.example new file mode 100644 index 0000000..01bb64a --- /dev/null +++ b/Data/dstarrepeater.ini.example @@ -0,0 +1,221 @@ +# D-Star Repeater configuration file +# Format: MMDVMHost-style INI, [Section] headers, Key=Value pairs, # comments +# Copy this file to /etc/dstarrepeater/dstarrepeater.ini and edit for your installation. + +[General] +Callsign=GB3IN C +Gateway= +# Operating mode: 0=Duplex, 1=Simplex, 2=Gateway, 3=TXOnly, 4=RXOnly, 5=TX+RX +Mode=0 +# Acknowledgement type: 0=None, 1=BER, 2=Status +Ack=1 +Restriction=0 +RPT1Validation=1 +DTMFBlanking=1 +ErrorReply=1 + +[Log] +# Logging levels: 0=None, 1=Debug, 2=Message, 3=Info, 4=Warning, 5=Error, 6=Fatal +FilePath=/var/log +FileLevel=2 +DisplayLevel=2 +MQTTLevel=2 + +[Paths] +Data=/usr/share/dstarrepeater +Audio=/var/log + +[Network] +GatewayAddress=127.0.0.1 +GatewayPort=20010 +LocalAddress=127.0.0.1 +LocalPort=20011 +Name= + +[Modem] +# Type: DVAP, GMSK Modem, DV-RPTR V1, DV-RPTR V2, DV-RPTR V3, DVMEGA, MMDVM, +# Sound Card, Split, Icom Access Point/Terminal Mode +Type=DVAP + +[Times] +# Transmission timeout in seconds +Timeout=180 +# Acknowledgement delay in milliseconds +AckTime=500 + +[Beacon] +# Beacon interval in seconds (0 to disable) +Time=600 +Text=D-Star Repeater +# 0=Text only, 1=Voice +Voice=0 +# Language: 0=English UK, 1=Deutsch, 2=Dansk, 3=Francais, 4=Italiano, 5=Polski, +# 6=Espanol, 7=Svenska, 8=Nederlands, 9=English US, 10=Norsk +Language=0 + +[Announcement] +Enabled=0 +Time=500 +RecordRPT1= +RecordRPT2= +DeleteRPT1= +DeleteRPT2= + +[Control] +Enabled=0 +RPT1= +RPT2= +Shutdown= +Startup= +Status1= +Status2= +Status3= +Status4= +Status5= +Command1= +Command1Line= +Command2= +Command2Line= +Command3= +Command3Line= +Command4= +Command4Line= +Command5= +Command5Line= +Command6= +Command6Line= +Output1= +Output2= +Output3= +Output4= + +[Controller] +# Type: (empty for none), GPIO, UDRC, Velleman K8055 - 0, URI USB - 1, +# Serial - /dev/ttyUSB0, Arduino - /dev/ttyUSB0 +Type= +# Serial configuration bitmask +SerialConfig=1 +PTTInvert=0 +ActiveHangTime=0 + +[Outputs] +Output1=0 +Output2=0 +Output3=0 +Output4=0 + +[Frame Logging] +Enabled=0 + +[Whitelist] +# Full path to whitelist file; leave commented out if not used +# File=rptr_whitelist.dat + +[Blacklist] +# Full path to blacklist file; leave commented out if not used +# File=rptr_blacklist.dat + +[Greylist] +# Full path to greylist file; leave commented out if not used +# File=greylist.dat + +[DVAP] +Port= +# Frequency in Hz +Frequency=145500000 +# Power in dBm +Power=10 +# Squelch threshold in dBm +Squelch=-100 + +[GMSK] +# Interface type: 0=LibUSB, 1=Serial +InterfaceType=0 +# USB address in decimal (0x0300 = 768) +Address=768 + +[DV-RPTR V1] +Port= +RXInvert=0 +TXInvert=0 +# Channel: 0=A, 1=B +Channel=0 +ModLevel=20 +TXDelay=150 + +[DV-RPTR V2] +# Connection type: 0=USB, 1=Network +Connection=0 +USBPort= +Address=127.0.0.1 +Port=0 +TXInvert=0 +ModLevel=20 +TXDelay=150 + +[DV-RPTR V3] +# Connection type: 0=USB, 1=Network +Connection=0 +USBPort= +Address=127.0.0.1 +Port=0 +TXInvert=0 +ModLevel=20 +TXDelay=150 + +[DVMEGA] +Port= +# Variant: 0=Modem, 1=Radio 2M, 2=Radio 70cm, 3=Radio 2M/70cm +Variant=0 +RXInvert=0 +TXInvert=0 +TXDelay=150 +# Frequencies in Hz +RXFrequency=145500000 +TXFrequency=145500000 +# Power level 0-100 +Power=100 + +[MMDVM] +Port= +RXInvert=0 +TXInvert=0 +PTTInvert=0 +# TX delay in milliseconds +TXDelay=50 +# Levels 0-100 +RXLevel=100 +TXLevel=100 + +[Sound Card] +RXDevice= +TXDevice= +RXInvert=0 +TXInvert=0 +RXLevel=1.0 +TXLevel=1.0 +TXDelay=150 +TXTail=50 + +[Split] +LocalAddress= +LocalPort=0 +# Timeout in milliseconds +Timeout=0 +# TX/RX names (TXName0-TXName4, RXName0-RXName24) +# TXName0= +# RXName0= + +[Icom] +Port= + +[MQTT] +Host=127.0.0.1 +Port=1883 +# Auth: 0=None, 1=Username/Password +Auth=0 +Username= +Password= +# Keepalive interval in seconds +Keepalive=60 +Name=dstar-repeater diff --git a/GUICommon/AddressTextCtrl.cpp b/GUICommon/AddressTextCtrl.cpp deleted file mode 100644 index c40cfce..0000000 --- a/GUICommon/AddressTextCtrl.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2002,2003,2009 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "AddressTextCtrl.h" - -CAddressTextCtrl::CAddressTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : -CRestrictedTextCtrl(parent, id, value, pos, size, style, ADDRESS_CHARS) -{ -} - -CAddressTextCtrl::~CAddressTextCtrl() -{ -} - diff --git a/GUICommon/AddressTextCtrl.h b/GUICommon/AddressTextCtrl.h deleted file mode 100644 index a0c99c5..0000000 --- a/GUICommon/AddressTextCtrl.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2002,2003,2009 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef AddressTextCtrl_H -#define AddressTextCtrl_H - -#include - -#include "RestrictedTextCtrl.h" - -const wxString ADDRESS_CHARS = wxT("0123456789."); - -class CAddressTextCtrl : public CRestrictedTextCtrl { - -public: - CAddressTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); - virtual ~CAddressTextCtrl(); - - private: -}; - -#endif diff --git a/GUICommon/CallsignTextCtrl.cpp b/GUICommon/CallsignTextCtrl.cpp deleted file mode 100644 index f37b11a..0000000 --- a/GUICommon/CallsignTextCtrl.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2002,2003 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "CallsignTextCtrl.h" - -CCallsignTextCtrl::CCallsignTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : -CRestrictedTextCtrl(parent, id, value, pos, size, style, CALLSIGN_CHARS) -{ -} - -CCallsignTextCtrl::~CCallsignTextCtrl() -{ -} - diff --git a/GUICommon/CallsignTextCtrl.h b/GUICommon/CallsignTextCtrl.h deleted file mode 100644 index 70a422e..0000000 --- a/GUICommon/CallsignTextCtrl.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2002,2003 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef CallsignTextCtrl_H -#define CallsignTextCtrl_H - -#include - -#include "RestrictedTextCtrl.h" - -const wxString CALLSIGN_CHARS = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/ "); - -class CCallsignTextCtrl : public CRestrictedTextCtrl { - -public: - CCallsignTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); - virtual ~CCallsignTextCtrl(); - - private: -}; - -#endif diff --git a/GUICommon/GUICommon.vcxproj b/GUICommon/GUICommon.vcxproj deleted file mode 100644 index 61c4878..0000000 --- a/GUICommon/GUICommon.vcxproj +++ /dev/null @@ -1,151 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {2C548AAB-A9F7-496F-BCF5-6185BDA304D2} - GUICommon - Win32Proj - 10.0 - - - - StaticLibrary - v142 - Unicode - true - - - StaticLibrary - v142 - Unicode - true - - - StaticLibrary - v142 - Unicode - - - StaticLibrary - v142 - Unicode - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>14.0.24720.0 - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswu - - - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswu - - - - Disabled - $(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - - Level3 - EditAndContinue - - - - - Disabled - $(WXWIN)\include\msvc;$(WXWIN)\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebugDLL - - - Level3 - ProgramDatabase - - - - - MaxSpeed - true - $(SolutionDir)..\WinUSB;$(SolutionDir)..\HID;$(SolutionDir)..\portaudio\include;C:\wxWidgets-2.8.12\include\msvc;C:\wxWidgets-2.8.12\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - Level3 - ProgramDatabase - - - - - MaxSpeed - true - $(SolutionDir)..\WinUSB;$(SolutionDir)..\HID;$(SolutionDir)..\portaudio\include;C:\wxWidgets-2.8.12\include\msvc;C:\wxWidgets-2.8.12\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - MultiThreadedDLL - true - - - Level3 - ProgramDatabase - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/GUICommon/GUICommon.vcxproj.filters b/GUICommon/GUICommon.vcxproj.filters deleted file mode 100644 index 2720603..0000000 --- a/GUICommon/GUICommon.vcxproj.filters +++ /dev/null @@ -1,47 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/GUICommon/Makefile b/GUICommon/Makefile deleted file mode 100644 index 6fb4eb7..0000000 --- a/GUICommon/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -OBJECTS = AddressTextCtrl.o CallsignTextCtrl.o MessageTextCtrl.o PortTextCtrl.o RestrictedTextCtrl.o - -.PHONY: all clean -all: GUICommon.a - -GUICommon.a: $(OBJECTS) - $(AR) rcs GUICommon.a $(OBJECTS) - --include $(OBJECTS:.o=.d) - -%.o: %.cpp - $(CXX) $(CFLAGS) -c -o $@ $< - $(CXX) -MM $(CFLAGS) $< > $*.d - -clean: - $(RM) GUICommon.a *.o *.d *.bak *~ - diff --git a/GUICommon/MessageTextCtrl.cpp b/GUICommon/MessageTextCtrl.cpp deleted file mode 100644 index ff67dbb..0000000 --- a/GUICommon/MessageTextCtrl.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2009 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "MessageTextCtrl.h" - -CMessageTextCtrl::CMessageTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : -CRestrictedTextCtrl(parent, id, value, pos, size, style, MSG_CHARS) -{ -} - -CMessageTextCtrl::~CMessageTextCtrl() -{ -} - diff --git a/GUICommon/MessageTextCtrl.h b/GUICommon/MessageTextCtrl.h deleted file mode 100644 index 970458a..0000000 --- a/GUICommon/MessageTextCtrl.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2009 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef MessageTextCtrl_H -#define MessageTextCtrl_H - -#include - -#include "RestrictedTextCtrl.h" - -const wxString MSG_CHARS = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ "); - -class CMessageTextCtrl : public CRestrictedTextCtrl { - -public: - CMessageTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); - virtual ~CMessageTextCtrl(); - - private: -}; - -#endif diff --git a/GUICommon/PortTextCtrl.cpp b/GUICommon/PortTextCtrl.cpp deleted file mode 100644 index 51435d9..0000000 --- a/GUICommon/PortTextCtrl.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2002,2003,2009 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "PortTextCtrl.h" - -CPortTextCtrl::CPortTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : -CRestrictedTextCtrl(parent, id, value, pos, size, style, PORT_CHARS) -{ -} - -CPortTextCtrl::~CPortTextCtrl() -{ -} - diff --git a/GUICommon/PortTextCtrl.h b/GUICommon/PortTextCtrl.h deleted file mode 100644 index 8d096c8..0000000 --- a/GUICommon/PortTextCtrl.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2002,2003,2009 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef PortTextCtrl_H -#define PortTextCtrl_H - -#include - -#include "RestrictedTextCtrl.h" - -const wxString PORT_CHARS = wxT("0123456789"); - -class CPortTextCtrl : public CRestrictedTextCtrl { - -public: - CPortTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); - virtual ~CPortTextCtrl(); - - private: -}; - -#endif diff --git a/GUICommon/RestrictedTextCtrl.cpp b/GUICommon/RestrictedTextCtrl.cpp deleted file mode 100644 index a291044..0000000 --- a/GUICommon/RestrictedTextCtrl.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2002,2003,2009 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "RestrictedTextCtrl.h" - -CRestrictedTextCtrl::CRestrictedTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style, const wxString& wantedChars) : -wxTextCtrl() -{ - wxASSERT(parent != NULL); - - wxArrayString charList; - - for (unsigned int i = 0; i < wantedChars.Length(); i++) - charList.Add(wantedChars.Mid(i, 1)); - - wxTextValidator validator(wxFILTER_INCLUDE_CHAR_LIST); - validator.SetIncludes(charList); - - Create(parent, id, value, pos, size, style, validator); -} - -CRestrictedTextCtrl::~CRestrictedTextCtrl() -{ -} - diff --git a/GUICommon/RestrictedTextCtrl.h b/GUICommon/RestrictedTextCtrl.h deleted file mode 100644 index f3d57b6..0000000 --- a/GUICommon/RestrictedTextCtrl.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2002,2003 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef RestrictedTextCtrl_H -#define RestrictedTextCtrl_H - -#include - -class CRestrictedTextCtrl : public wxTextCtrl { -public: - CRestrictedTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style, const wxString& wantedChars); - virtual ~CRestrictedTextCtrl(); - -private: -}; - -#endif diff --git a/MQTT.md b/MQTT.md index ae9f647..50e358c 100644 --- a/MQTT.md +++ b/MQTT.md @@ -1,76 +1,70 @@ # MQTT Support for DStarRepeater -DStarRepeater can optionally publish log messages and repeater status to -an MQTT broker, providing the same kind of live telemetry that MMDVMHost -offers. This makes it possible to monitor D-Star repeater activity from -dashboards, home-automation systems, or any MQTT client. +DStarRepeater can publish log messages and repeater status to an MQTT +broker, providing live telemetry for dashboards, home-automation systems, +or any MQTT client. -**MQTT support is entirely optional.** When built without the MQTT flag -the compiled binary is identical to the tree before MQTT was added — no -MQTT code is included and no additional libraries are required. +**MQTT is enabled by default.** To build without MQTT support, use `make MQTT=0`. +When built without MQTT, no MQTT code is compiled in and `libmosquitto` is not required. -## Building - -### Without MQTT (default) - -``` -make -``` +A full JSON schema for all MQTT messages is available in [schema.json](schema.json). -Produces the same `dstarrepeaterd` as the original codebase. No -`libmosquitto` dependency. - -### With MQTT +## Building -Install `libmosquitto-dev` first: +MQTT is on by default. Install `libmosquitto-dev` before building: -``` +```bash sudo apt-get install libmosquitto-dev +make ``` -Then build with the `MQTT` flag: +To build without MQTT: -``` -make MQTT=1 +```bash +make MQTT=0 ``` -This adds `-DMQTT` to the compiler flags and links against -`-lmosquitto`. All MQTT code paths are guarded by -`#if defined(MQTT)` / `#endif`, so the flag acts as a clean on/off -switch. +All MQTT code paths are guarded by `#if defined(MQTT)` / `#endif`. ## Configuration -Add the following keys to your `dstarrepeater` configuration file -(the same flat key=value format used by all other settings): - +MQTT settings are in the `[MQTT]` section of the INI config file: + +```ini +[MQTT] +Host=127.0.0.1 +Port=1883 +Auth=0 +Username= +Password= +Keepalive=60 +Name=dstar-repeater ``` -mqttHost=127.0.0.1 -mqttPort=1883 -mqttAuth=0 -mqttUsername= -mqttPassword= -mqttKeepalive=60 -mqttName=dstar-repeater + +| Key | Default | Description | +|-----|---------|-------------| +| `Host` | `127.0.0.1` | MQTT broker hostname or IP address. Empty to disable. | +| `Port` | `1883` | MQTT broker port. | +| `Auth` | `0` | `1` = authenticate with username/password. | +| `Username` | *(empty)* | Broker username (when Auth=1). | +| `Password` | *(empty)* | Broker password (when Auth=1). | +| `Keepalive` | `60` | Keepalive interval in seconds. | +| `Name` | `dstar-repeater` | Client name and MQTT topic prefix. | + +The MQTT log level is configured in the `[Log]` section: + +```ini +[Log] +MQTTLevel=2 ``` -| Key | Default | Description | -|-----------------|-------------------|----------------------------------------------------| -| `mqttHost` | `127.0.0.1` | MQTT broker hostname or IP address | -| `mqttPort` | `1883` | MQTT broker port | -| `mqttAuth` | `0` | Enable authentication (`0` = off, `1` = on) | -| `mqttUsername` | *(empty)* | Username when `mqttAuth=1` | -| `mqttPassword` | *(empty)* | Password when `mqttAuth=1` | -| `mqttKeepalive` | `60` | Keepalive interval in seconds | -| `mqttName` | `dstar-repeater` | Client name; also used as the MQTT topic prefix | +If `Host` is left empty, MQTT is disabled at runtime even when compiled in. -If `mqttHost` is left empty, MQTT is disabled at runtime even when -compiled in. +See [CONFIGURATION.md](CONFIGURATION.md) for the full configuration reference. ## MQTT Topics -All topics are automatically prefixed with the value of `mqttName`. -For example, with the default name `dstar-repeater`: +All topics are prefixed with the `Name` value. With the default `dstar-repeater`: ### `dstar-repeater/log` @@ -78,33 +72,29 @@ Timestamped log messages, filtered by severity. The format matches the log file output: ``` -M: 2025-03-08 14:22:01: Starting D-Star Repeater - 20180403 -M: 2025-03-08 14:22:01: Using wxWidgets 3.0.5 on Linux 6.1.21-v8+ -M: 2025-03-08 14:22:01: Callsign set to "GB7XX B", gateway set to "GB7XX G" -M: 2025-03-08 14:22:01: Modem type set to "MMDVM" -I: 2025-03-08 14:22:01: MQTT connected to 127.0.0.1:1883 as dstar-repeater -M: 2025-03-08 14:22:01: Starting the D-Star repeater thread -M: 2025-03-08 14:22:01: Poll text set to "linux_mmdvm-20180403" +M: 2026-03-19 14:22:01: Starting D-Star Repeater - 20260319 +M: 2026-03-19 14:22:01: Using Linux 6.1.21-v8+ on aarch64 +M: 2026-03-19 14:22:01: Callsign set to "GB7XX B", gateway set to "GB7XX G" +I: 2026-03-19 14:22:01: MQTT connected to 127.0.0.1:1883 as dstar-repeater ``` Log levels (highest to lowest): -| Letter | Level | Numeric | -|--------|---------------|---------| -| `F` | Fatal | 6 | -| `E` | Error | 5 | -| `W` | Warning | 4 | -| `I` | Info | 3 | -| `M` | Message | 2 | -| `D` | Debug | 1 | +| Letter | Level | Numeric | +|--------|-------|---------| +| `F` | Fatal | 6 | +| `E` | Error | 5 | +| `W` | Warning | 4 | +| `I` | Info | 3 | +| `M` | Message | 2 | +| `D` | Debug | 1 | -Only messages at or above the configured threshold are published. The -default threshold is **2** (Message level), matching MMDVMHost behaviour. +Only messages at or above the configured `MQTTLevel` are published. +The default is **2** (Message level), matching MMDVMHost behaviour. ### `dstar-repeater/status` -A JSON object published once per second with the current repeater state. -This is the D-Star equivalent of MMDVMHost's status output: +A JSON object published once per second with the current repeater state: **Idle (no traffic):** ```json @@ -148,56 +138,35 @@ This is the D-Star equivalent of MMDVMHost's status output: } ``` -**During a network (gateway) transmission:** -```json -{ - "myCall1": "G4KLX ", - "myCall2": " ", - "yourCall": "GB7XX B", - "rptCall1": "GB7XX B", - "rptCall2": "GB7XX G", - "tx": true, - "rxState": "listening", - "rptState": "network", - "ber": 0.0, - "text": "", - "status1": "", - "status2": "", - "status3": "", - "status4": "", - "status5": "" -} -``` - #### Status Field Reference -| Field | Type | Description | -|------------|---------|-----------------------------------------------------------| -| `myCall1` | string | Transmitting station's callsign (8 chars, space-padded) | -| `myCall2` | string | Transmitting station's short suffix (4 chars) | -| `yourCall` | string | Destination callsign (`CQCQCQ` for CQ calls) | -| `rptCall1` | string | Repeater callsign with module letter | -| `rptCall2` | string | Gateway callsign | -| `tx` | boolean | `true` when the repeater is transmitting | -| `rxState` | string | `listening`, `process_data`, or `process_slow_data` | -| `rptState` | string | See repeater states below | -| `ber` | float | Bit Error Rate as a percentage (0.0 when idle) | -| `text` | string | Slow-data text message (if any) | -| `status1`–`status5` | string | User-configured status text messages | +| Field | Type | Description | +|-------|------|-------------| +| `myCall1` | string | Transmitting station's callsign (8 chars, space-padded) | +| `myCall2` | string | Transmitting station's short suffix (4 chars) | +| `yourCall` | string | Destination callsign (`CQCQCQ` for CQ calls) | +| `rptCall1` | string | Repeater callsign with module letter | +| `rptCall2` | string | Gateway callsign | +| `tx` | boolean | `true` when the repeater is transmitting | +| `rxState` | string | `listening`, `process_data`, or `process_slow_data` | +| `rptState` | string | See repeater states below | +| `ber` | float | Bit Error Rate as a percentage (0.0 when idle) | +| `text` | string | Slow-data text message (if any) | +| `status1`–`status5` | string | User-configured status text messages | #### Repeater States (`rptState`) -| Value | Meaning | -|----------------|------------------------------------------------------| -| `listening` | Idle, waiting for traffic | -| `valid` | Receiving valid RF traffic | -| `valid_wait` | Valid RF transmission ended, waiting for ack window | -| `invalid` | Receiving RF traffic that failed validation | -| `invalid_wait` | Invalid RF ended, waiting | -| `timeout` | Transmission timed out | -| `timeout_wait` | Timeout ended, waiting | -| `network` | Receiving traffic from the gateway/network | -| `shutdown` | Repeater is shut down | +| Value | Meaning | +|-------|---------| +| `listening` | Idle, waiting for traffic | +| `valid` | Receiving valid RF traffic | +| `valid_wait` | Valid RF ended, waiting for ack window | +| `invalid` | Receiving RF that failed validation | +| `invalid_wait` | Invalid RF ended, waiting | +| `timeout` | Transmission timed out | +| `timeout_wait` | Timeout ended, waiting | +| `network` | Receiving traffic from the gateway | +| `shutdown` | Repeater is shut down | ### `dstar-repeater/json` @@ -206,20 +175,11 @@ format expected by [Display-Driver](https://github.com/MW0MWZ/Display-Driver). This means DStarRepeater can drive an OLED/TFT display via MQTT with no modifications to Display-Driver. -Unlike the `status` topic (polled once per second), the `json` topic only -publishes when something actually changes — a transmission starts, ends, -or is lost. - **RF transmission starts:** ```json {"D-Star":{"action":"start","source_cs":"MW0MWZ ","source_ext":" ","destination_cs":"CQCQCQ ","reflector":"GB7XX G","source":"rf"}} ``` -**Network transmission starts:** -```json -{"D-Star":{"action":"start","source_cs":"G4KLX ","source_ext":" ","destination_cs":"GB7XX B","reflector":"GB7XX G","source":"net"}} -``` - **Transmission ends normally:** ```json {"D-Star":{"action":"end"}} @@ -230,33 +190,37 @@ or is lost. {"D-Star":{"action":"lost"}} ``` -**Return to idle (sent after every end/lost):** +**Return to idle:** ```json {"MMDVM":{"mode":"idle"}} ``` -**BER update (published once per second during active RF):** +**BER update (once per second during active RF):** ```json {"BER":{"mode":"D-Star","value":1.3}} ``` +**RSSI update (DVAP modem, once per second during active RF):** +```json +{"RSSI":{"mode":"D-Star","value":-85}} +``` + #### Display-Driver Compatibility These messages use the exact same JSON keys and structure that -Display-Driver expects. If your `mqttName` is set to `dstar-repeater`, -configure Display-Driver to subscribe to `dstar-repeater/json`. - -Display-Driver dispatches each top-level key to a specific parser: +Display-Driver expects. Configure Display-Driver to subscribe to +`dstar-repeater/json`. -| Top-level key | Display-Driver parser | When published | -|---------------|----------------------|------------------------------------------| -| `D-Star` | `parseDStar()` | Transmission start, end, or lost | -| `BER` | `parseBER()` | Once per second during active RF | -| `MMDVM` | `parseMMDVM()` | On return to idle | +| Top-level key | Display-Driver parser | When published | +|---------------|----------------------|----------------| +| `D-Star` | `parseDStar()` | Transmission start, end, or lost | +| `BER` | `parseBER()` | Once per second during active RF | +| `RSSI` | `parseRSSI()` | Once per second during active RF (DVAP only) | +| `MMDVM` | `parseMMDVM()` | On return to idle | ## Subscribing to MQTT Output -Use any MQTT client to subscribe. For example, with `mosquitto_sub`: +Use any MQTT client to subscribe: ```bash # Follow all DStarRepeater topics: @@ -265,51 +229,35 @@ mosquitto_sub -h 127.0.0.1 -t "dstar-repeater/#" # Log messages only: mosquitto_sub -h 127.0.0.1 -t "dstar-repeater/log" -# Status JSON only: -mosquitto_sub -h 127.0.0.1 -t "dstar-repeater/status" - -# Display-Driver-compatible events: -mosquitto_sub -h 127.0.0.1 -t "dstar-repeater/json" - # Pretty-print status with jq: mosquitto_sub -h 127.0.0.1 -t "dstar-repeater/status" | jq . - -# Pretty-print Display-Driver events with jq: -mosquitto_sub -h 127.0.0.1 -t "dstar-repeater/json" | jq . ``` ## Comparison with MMDVMHost -This implementation follows the same conventions as MMDVMHost's MQTT -support: +This implementation follows MMDVMHost's MQTT conventions: - Same `CMQTTConnection` class wrapping `libmosquitto` - Same topic-prefix convention (`{name}/topic`) +- Same INI-style `[MQTT]` configuration section - Same log-level filtering and format -- Same PID-based client ID scheme (avoids the `time_t` truncation - issue on 32-bit ARM) -- Same configuration key names (`mqttHost`, `mqttPort`, etc.) +- Same PID-based client ID scheme - Same QoS default (EXACTLY_ONCE / QoS 2) -The key difference is that DStarRepeater is a **publish-only** client — -it does not subscribe to any MQTT topics or accept remote commands via -MQTT. The configuration file format also differs: DStarRepeater uses -flat `key=value` pairs rather than MMDVMHost's INI-style `[MQTT]` -section. +DStarRepeater is a **publish-only** client — it does not subscribe to +any MQTT topics or accept remote commands via MQTT. ## Troubleshooting **"Unable to start MQTT connection"** in logs: -- Check that Mosquitto (or another MQTT broker) is running on the - configured host and port -- Verify credentials if `mqttAuth=1` +- Check that Mosquitto (or another MQTT broker) is running +- Verify credentials if `Auth=1` - Check firewall rules if connecting to a remote broker **No messages appearing:** -- Confirm the binary was built with `make MQTT=1` -- Check that `mqttHost` is not empty in the config file -- Verify the broker is accessible: `mosquitto_pub -h 127.0.0.1 -t test -m hello` +- Confirm the binary was built with MQTT support (default: on) +- Check that `Host` is not empty in the `[MQTT]` section +- Verify the broker: `mosquitto_pub -h 127.0.0.1 -t test -m hello` **Log messages missing but status works:** -- The log level threshold may be filtering them out. Lower-priority - messages (Debug, Trace) are not published by default. +- The `MQTTLevel` in `[Log]` may be filtering them. Default is 2 (Message). diff --git a/Makefile b/Makefile index 304f873..bf7ba3f 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,37 @@ .PHONY: install installdirs clean force -export BUILD ?= debug + +export BUILD ?= release export DATADIR ?= /usr/share/dstarrepeater -export LOGDIR ?= /var/log -export CONFDIR ?= /etc +export CONFDIR ?= /etc/dstarrepeater export BINDIR ?= /usr/bin +# MQTT is on by default. Use MQTT=0 to disable. +MQTT ?= 1 + +# GPIO is on by default for ARM Linux (Raspberry Pi). Use GPIO=0 to disable, +# or GPIO=1 to force-enable on other platforms. +ARCH := $(shell uname -m) +OS := $(shell uname -s) +ifeq ($(OS),Linux) + ifneq (,$(filter arm% aarch64,$(ARCH))) + GPIO ?= 1 + else + GPIO ?= 0 + endif +else + GPIO ?= 0 +endif + DEBUGFLAGS := -g -D_DEBUG -RELEASEFLAGS := -DNDEBUG -DwxDEBUG_LEVEL=0 -export CXX := $(shell wx-config --cxx) -export CFLAGS := -O2 -Wall $(shell wx-config --cxxflags) -DLOG_DIR='"$(LOGDIR)"' -DCONF_DIR='"$(CONFDIR)"' -DDATA_DIR='"$(DATADIR)"' +RELEASEFLAGS := -DNDEBUG +export CXX := g++ +export CFLAGS := -std=c++17 -O2 -Wall ifeq ($(BUILD), debug) export CFLAGS := $(CFLAGS) $(DEBUGFLAGS) else ifeq ($(BUILD), release) export CFLAGS := $(CFLAGS) $(RELEASEFLAGS) endif -export GUILIBS := $(shell wx-config --libs adv,core,base) -lasound -export LIBS := $(shell wx-config --libs base) -lasound -lusb-1.0 +export LIBS := -lpthread -lasound -lusb-1.0 export LDFLAGS := ifeq ($(MQTT), 1) @@ -23,33 +39,30 @@ ifeq ($(MQTT), 1) export LIBS := $(LIBS) -lmosquitto endif -all: DStarRepeater/dstarrepeaterd DStarRepeaterConfig/dstarrepeaterconfig +ifeq ($(GPIO), 1) + export CFLAGS := $(CFLAGS) -DGPIO + export LIBS := $(LIBS) -lwiringPi +endif + +all: DStarRepeater/dstarrepeaterd DStarRepeater/dstarrepeaterd: Common/Common.a force $(MAKE) -C DStarRepeater -DStarRepeaterConfig/dstarrepeaterconfig: GUICommon/GUICommon.a Common/Common.a force - $(MAKE) -C DStarRepeaterConfig - -GUICommon/GUICommon.a: force - $(MAKE) -C GUICommon - Common/Common.a: force $(MAKE) -C Common -installdirs: - /bin/mkdir -p $(DESTDIR)$(DATADIR) $(DESTDIR)$(LOGDIR) $(DESTDIR)$(CONFDIR) $(DESTDIR)$(BINDIR) +installdirs: + /bin/mkdir -p $(DESTDIR)$(DATADIR) $(DESTDIR)$(CONFDIR) $(DESTDIR)$(BINDIR) install: all installdirs $(MAKE) -C Data install $(MAKE) -C DStarRepeater install - $(MAKE) -C DStarRepeaterConfig install + install -g root -o root -m 0644 Data/dstarrepeater.ini.example $(DESTDIR)$(CONFDIR)/dstarrepeater.ini.example clean: $(MAKE) -C Common clean - $(MAKE) -C GUICommon clean $(MAKE) -C DStarRepeater clean - $(MAKE) -C DStarRepeaterConfig clean force: @true diff --git a/MakefileGUI b/MakefileGUI deleted file mode 100644 index 7e20f01..0000000 --- a/MakefileGUI +++ /dev/null @@ -1,52 +0,0 @@ -export BUILD ?= debug -export DATADIR ?= /usr/share/dstarrepeater -export LOGDIR ?= /var/log -export CONFDIR ?= /etc -export BINDIR ?= /usr/bin - -DEBUGFLAGS := -g -D_DEBUG -RELEASEFLAGS := -DNDEBUG -DwxDEBUG_LEVEL=0 - -export CXX := $(shell wx-config --cxx) -export CFLAGS := -O2 -Wall $(shell wx-config --cxxflags) -DLOG_DIR='"$(LOGDIR)"' -DCONF_DIR='"$(CONFDIR)"' -DDATA_DIR='"$(DATADIR)"' -ifeq ($(BUILD), debug) - export CFLAGS := $(CFLAGS) $(DEBUGFLAGS) -else ifeq ($(BUILD), release) - export CFLAGS := $(CFLAGS) $(RELEASEFLAGS) -endif -export GUILIBS := $(shell wx-config --libs adv,core,base) -lasound -lusb-1.0 -export LIBS := $(shell wx-config --libs base) -lasound -lusb-1.0 -export LDFLAGS := - -.PHONY: install installdirs clean force - -all: DStarRepeater/dstarrepeater DStarRepeaterConfig/dstarrepeaterconfig - -DStarRepeater/dstarrepeater: GUICommon/GUICommon.a Common/Common.a force - $(MAKE) -C DStarRepeater -f MakefileGUI - -DStarRepeaterConfig/dstarrepeaterconfig: GUICommon/GUICommon.a Common/Common.a force - $(MAKE) -C DStarRepeaterConfig - -GUICommon/GUICommon.a: force - $(MAKE) -C GUICommon - -Common/Common.a: force - $(MAKE) -C Common - -installdirs: - /bin/mkdir -p $(DESTDIR)$(DATADIR) $(DESTDIR)$(LOGDIR) $(DESTDIR)$(CONFDIR) $(DESTDIR)$(BINDIR) - -install: all installdirs - $(MAKE) -C Data install - $(MAKE) -C DStarRepeater -f MakefileGUI install - $(MAKE) -C DStarRepeaterConfig install - -clean: - $(MAKE) -C Common clean - $(MAKE) -C GUICommon clean - $(MAKE) -C DStarRepeater -f MakefileGUI clean - $(MAKE) -C DStarRepeaterConfig clean - -force: - @true diff --git a/MakefileGUIPi b/MakefileGUIPi deleted file mode 100644 index dad022d..0000000 --- a/MakefileGUIPi +++ /dev/null @@ -1,41 +0,0 @@ -export DATADIR := "/usr/share/dstarrepeater" -export LOGDIR := "/var/log" -export CONFDIR := "/etc" -export BINDIR := "/usr/bin" - -export CXX := $(shell wx-config --cxx) -export CFLAGS := -O2 -Wall $(shell wx-config --cxxflags) -DLOG_DIR='$(LOGDIR)' -DCONF_DIR='$(CONFDIR)' -DDATA_DIR='$(DATADIR)' -DGPIO -export GUILIBS := $(shell wx-config --libs adv,core,base) -lasound -lusb-1.0 -lwiringPi -export LIBS := $(shell wx-config --libs base) -lasound -lwiringPi -export LDFLAGS := - -all: DStarRepeater/dstarrepeater DStarRepeaterConfig/dstarrepeaterconfig - -DStarRepeater/dstarrepeater: GUICommon/GUICommon.a Common/Common.a force - $(MAKE) -C DStarRepeater -f MakefileGUI - -DStarRepeaterConfig/dstarrepeaterconfig: GUICommon/GUICommon.a Common/Common.a force - $(MAKE) -C DStarRepeaterConfig - -GUICommon/GUICommon.a: force - $(MAKE) -C GUICommon - -Common/Common.a: force - $(MAKE) -C Common - -.PHONY: install -install: all - $(MAKE) -C Data install - $(MAKE) -C DStarRepeater -f MakefileGUI install - $(MAKE) -C DStarRepeaterConfig install - -.PHONY: clean -clean: - $(MAKE) -C Common clean - $(MAKE) -C GUICommon clean - $(MAKE) -C DStarRepeater clean - $(MAKE) -C DStarRepeaterConfig clean - -.PHONY: force -force: - @true diff --git a/MakefilePi b/MakefilePi deleted file mode 100644 index 17645ff..0000000 --- a/MakefilePi +++ /dev/null @@ -1,41 +0,0 @@ -export DATADIR := "/usr/share/dstarrepeater" -export LOGDIR := "/var/log" -export CONFDIR := "/etc" -export BINDIR := "/usr/bin" - -export CXX := $(shell wx-config --cxx) -export CFLAGS := -O2 -Wall $(shell wx-config --cxxflags) -DLOG_DIR='$(LOGDIR)' -DCONF_DIR='$(CONFDIR)' -DDATA_DIR='$(DATADIR)' -DGPIO -export GUILIBS := $(shell wx-config --libs adv,core,base) -lasound -export LIBS := $(shell wx-config --libs base) -lasound -lusb-1.0 -lwiringPi -export LDFLAGS := - -all: DStarRepeater/dstarrepeaterd DStarRepeaterConfig/dstarrepeaterconfig - -DStarRepeater/dstarrepeaterd: Common/Common.a force - $(MAKE) -C DStarRepeater - -DStarRepeaterConfig/dstarrepeaterconfig: GUICommon/GUICommon.a Common/Common.a force - $(MAKE) -C DStarRepeaterConfig - -GUICommon/GUICommon.a: force - $(MAKE) -C GUICommon - -Common/Common.a: force - $(MAKE) -C Common - -.PHONY: install -install: all - $(MAKE) -C Data install - $(MAKE) -C DStarRepeater install - $(MAKE) -C DStarRepeaterConfig install - -.PHONY: clean -clean: - $(MAKE) -C Common clean - $(MAKE) -C GUICommon clean - $(MAKE) -C DStarRepeater clean - $(MAKE) -C DStarRepeaterConfig clean - -.PHONY: force -force: - @true diff --git a/README.md b/README.md index ca9a817..db12b47 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,217 @@ # DStarRepeater -DStarRepeater is the D-Star repeater controller for homebrew repeater hardware. It links into [ircDDBGateway](https://github.com/g4klx/ircDDBGateway) or the newer [DStarGateway](https://github.com/g4klx/DStarGateway) to provide access to D-Star networking facilities such as callsign routing, reflector linking, and ircDDB. +A D-Star digital voice repeater controller for homebrew amateur radio hardware. DStarRepeater interfaces with [ircDDBGateway](https://github.com/g4klx/ircDDBGateway) or [DStarGateway](https://github.com/g4klx/DStarGateway) to provide D-Star networking — callsign routing, reflector linking, and ircDDB. + +This is a headless CLI daemon written in portable C++17 with no GUI dependencies. It runs on Linux (x86_64, ARM/Raspberry Pi), Windows, and macOS, and has been tested on Ubuntu 24.04, Debian Trixie, and Alpine 3.21. ## Supported Hardware -- DVAP -- DV-Mega (D-Star only) -- GMSK Modems -- Soundcard repeaters (including UDRC) -- MMDVM (D-Star only) -- DV-RPTR V1, V2, and V3 -- Icom Terminal and Access Point modes +| Modem | Interface | +|-------|-----------| +| DVAP Dongle | USB serial | +| DV-Mega | USB serial | +| GMSK Modem (Dutch*STAR, DUTCH-UHF) | USB (libusb) | +| Sound Card / UDRC | ALSA (Linux), PortAudio (Windows/macOS) | +| MMDVM | USB serial | +| DV-RPTR V1, V2, V3 | USB serial or network | +| Icom Terminal / Access Point Mode | USB serial | +| Split (multi-receiver) | UDP network | + +## Quick Start -## Building +### 1. Install dependencies -Builds on 32-bit and 64-bit Linux as well as on Windows using Visual Studio 2017 (x86 and x64). +**Ubuntu / Debian:** +```bash +sudo apt-get install g++ make libasound2-dev libusb-1.0-0-dev libmosquitto-dev +``` -### Standard Build +**Raspberry Pi (Raspberry Pi OS / Debian):** +```bash +sudo apt-get install g++ make libasound2-dev libusb-1.0-0-dev libmosquitto-dev wiringpi +``` +**Alpine Linux:** +```bash +apk add g++ make alsa-lib-dev libusb-dev linux-headers mosquitto-dev ``` + +**Windows and macOS** — see the [platform-specific notes](#windows) below. + +### 2. Build + +```bash make ``` -### With MQTT Support +### 3. Configure + +Copy the example config and edit for your hardware: +```bash +sudo cp /etc/dstarrepeater/dstarrepeater.ini.example /etc/dstarrepeater/dstarrepeater.ini +sudo nano /etc/dstarrepeater/dstarrepeater.ini +``` + +See [CONFIGURATION.md](CONFIGURATION.md) for the full configuration reference. + +### 4. Install and run + +```bash +sudo make install +sudo dstarrepeaterd /etc/dstarrepeater/dstarrepeater.ini +``` + +## Build Options + +By default, `make` produces a **release build** with **MQTT enabled** and **GPIO enabled on ARM** (Raspberry Pi). Any option can be overridden on the command line. + +| Command | Description | +|---------|-------------| +| `make` | Default build (release, MQTT on, GPIO auto-detected) | +| `make MQTT=0` | Build without MQTT support | +| `make GPIO=0` | Build without GPIO support (even on ARM) | +| `make GPIO=1` | Force GPIO support on non-ARM platforms | +| `make BUILD=debug` | Debug build with symbols and assertions | +| `sudo make install` | Install binary, data files, and example config | +| `make clean` | Remove build artifacts | + +**Defaults:** + +| Option | Default | Notes | +|--------|---------|-------| +| `BUILD` | `release` | Optimised, no debug symbols | +| `MQTT` | `1` (on) | Requires `libmosquitto-dev` | +| `GPIO` | `1` on ARM, `0` otherwise | Requires `wiringPi`; auto-detected via `uname -m` | + +## Dependencies + +### Required (Linux) + +| Package | Ubuntu/Debian | Alpine | Purpose | +|---------|---------------|--------|---------| +| C++17 compiler | `g++` (GCC 13+) | `g++` | Compilation | +| GNU Make | `make` | `make` | Build system | +| ALSA dev libraries | `libasound2-dev` | `alsa-lib-dev` | Sound card modem support | +| libusb 1.0 dev | `libusb-1.0-0-dev` | `libusb-dev` | USB modem support (GMSK) | +| Mosquitto dev | `libmosquitto-dev` | `mosquitto-dev` | MQTT telemetry (enabled by default) | +| Linux headers | — | `linux-headers` | Required on Alpine only | + +### Raspberry Pi (additional) + +| Package | Install | Purpose | +|---------|---------|---------| +| wiringPi | `sudo apt-get install wiringpi` | GPIO controller support (enabled by default on ARM) | + +> **Note:** On newer Raspberry Pi OS releases where `wiringpi` is not in the repositories, install from the [unofficial mirror](https://github.com/WiringPi/WiringPi): +> ```bash +> git clone https://github.com/WiringPi/WiringPi.git +> cd WiringPi && ./build +> ``` + +### Disabling optional features + +If you don't need MQTT or GPIO support, you can skip those dependencies and disable them at build time: + +```bash +# Without MQTT (no libmosquitto-dev needed) +make MQTT=0 + +# Without GPIO on a Pi (no wiringPi needed) +make GPIO=0 + +# Without both +make MQTT=0 GPIO=0 +``` + +### Windows + +The codebase includes `#if defined(_WIN32)` blocks for Windows compatibility. A Windows build requires: + +| Dependency | Source | Notes | +|------------|--------|-------| +| Visual Studio 2019+ or MinGW-w64 | Microsoft / MSYS2 | Must support C++17 (`/std:c++17` or `-std=c++17`) | +| libusb 1.0 | [libusb.info](https://libusb.info/) | Windows binaries available; needed for GMSK modem support | +| PortAudio | [portaudio.com](http://www.portaudio.com/) | Required for sound card modem support (replaces ALSA) | +| Eclipse Mosquitto | [mosquitto.org](https://mosquitto.org/) | Optional; only needed for MQTT builds | + +The provided `Makefile` targets Linux. For Windows, you will need to create a Visual Studio project or CMakeLists.txt and link against `ws2_32.lib` (Winsock), `portaudio.lib`, and `libusb-1.0.lib`. Serial port, socket, and signal handling code uses Win32 APIs (`CreateFile`, `Winsock2`, `SetConsoleCtrlHandler`) that are included in the Windows SDK — no additional libraries are needed for those. + +**Note:** The K8055 (Velleman) and URI USB controllers are currently Linux-only (they use libusb directly). On Windows they require their respective vendor DLLs, which are not yet integrated. + +### macOS + +| Dependency | Source | Notes | +|------------|--------|-------| +| Xcode Command Line Tools | `xcode-select --install` | Provides clang with C++17 support | +| libusb 1.0 | `brew install libusb` | For GMSK modem support | +| PortAudio | `brew install portaudio` | For sound card modem support | +| Mosquitto | `brew install mosquitto` | Optional; only for MQTT builds | -DStarRepeater can optionally publish live telemetry to an MQTT broker, including log messages, repeater status, and Display-Driver-compatible events for driving OLED/TFT displays. +The `Makefile` currently targets Linux. A macOS build would need adjusted compiler flags and library paths (e.g. via `pkg-config` or Homebrew paths). The endian handling and serial port code already support macOS. + +## Command-Line Usage ``` -sudo apt-get install libmosquitto-dev -make MQTT=1 +dstarrepeaterd ``` -MQTT support is entirely opt-in. When built without the `MQTT=1` flag, the binary is identical to the tree before MQTT was added — no MQTT code is compiled in and no additional libraries are required. +The config file path is the only argument. All settings including log directory, verbosity levels, audio paths, and callsign lists are configured in the config file itself. + +```bash +# Single instance +dstarrepeaterd /etc/dstarrepeater/dstarrepeater.ini + +# Multiple instances with separate configs +dstarrepeaterd /etc/dstarrepeater/gb3in.ini +dstarrepeaterd /etc/dstarrepeater/gb7xx.ini +``` -See [MQTT.md](MQTT.md) for full configuration details, topic structure, JSON examples, and Display-Driver compatibility information. +## Running as a System Service + +A systemd service template is provided in the `debian/` directory. The instance name maps to the config filename: + +```bash +sudo cp debian/dstarrepeaterd.dstarrepeaterd@.service /etc/systemd/system/dstarrepeaterd@.service +sudo systemctl daemon-reload + +# Start with /etc/dstarrepeater/gb3in.ini +sudo systemctl enable dstarrepeaterd@gb3in +sudo systemctl start dstarrepeaterd@gb3in +``` + +The `%i` specifier expands to the instance name, so `dstarrepeaterd@gb3in` runs `dstarrepeaterd /etc/dstarrepeater/gb3in.ini`. + +## MQTT Telemetry + +When built with `make MQTT=1`, the daemon can publish live telemetry to an MQTT broker: + +- **Log messages** — timestamped log output +- **Repeater status** — JSON state updates (1/sec) +- **D-Star events** — start/end/lost/BER/text in Display-Driver-compatible JSON + +See [MQTT.md](MQTT.md) for full details on configuration, topic structure, and JSON format. + +## Documentation + +- [CONFIGURATION.md](CONFIGURATION.md) — Full configuration file reference +- [MQTT.md](MQTT.md) — MQTT telemetry setup and topic reference +- [BUILD.md](BUILD.md) — Detailed build instructions +- [CHANGELOG.md](CHANGELOG.md) — Version history + +## Architecture + +DStarRepeater runs as a single process with multiple threads: + +- **Repeater thread** — core D-Star protocol state machine (one of four variants based on operating mode: duplex/simplex, TX-only, RX-only, or TX+RX) +- **Modem thread** — hardware interface to the radio modem +- **Controller thread** — hardware I/O for PTT, heartbeat, and external control pins + +The repeater thread communicates with a gateway (ircDDBGateway or DStarGateway) via UDP using the DSRP protocol. ## Licence -This software is licenced under the GPL v2. +This software is licenced under the GPL v2. See [COPYING.txt](COPYING.txt). + +## Credits + +Originally written by Jonathan Naylor G4KLX. Based on the [original DStarRepeater](https://github.com/g4klx/DStarRepeater) with wxWidgets dependency removed and ported to standalone C++17. diff --git a/WindowsUSB/dvrptr_cdc.inf b/WindowsUSB/dvrptr_cdc.inf deleted file mode 100644 index 83ed30c..0000000 --- a/WindowsUSB/dvrptr_cdc.inf +++ /dev/null @@ -1,76 +0,0 @@ -; Driver-Dummy for the Communication Device Class - -[Version] -Signature="$Windows NT$" -Class=Ports -ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} - -Provider=%DIGISOLUTIONS% -DriverVer=10/15/1999,5.0.2153.1 - -[Manufacturer] -%DIGISOLUTIONS%=DIGISOLUTIONS, NTamd64 - -[DIGISOLUTIONS] -%DVMODEM_CDC%=Reader, USB\VID_03EB&PID_2307 - -[DIGISOLUTIONS.NTamd64] -%DVMODEM_CDC%=DriverInstall, USB\VID_03EB&PID_2307 - -[Reader_Install.NTx86] - -[DestinationDirs] -DefaultDestDir=12 -Reader.NT.Copy=12 - -[Reader.NT] -include=mdmcpq.inf -CopyFiles=Reader.NT.Copy -AddReg=Reader.NT.AddReg - -[Reader.NT.Copy] -usbser.sys - -[Reader.NT.AddReg] -HKR,,DevLoader,,*ntkern -HKR,,NTMPDriver,,usbser.sys -HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" - -[Reader.NT.Services] -AddService = usbser, 0x00000002, Service_Inst - -[Service_Inst] -DisplayName = %Serial.SvcDesc% -ServiceType = 1 ; SERVICE_KERNEL_DRIVER -StartType = 3 ; SERVICE_DEMAND_START -ErrorControl = 1 ; SERVICE_ERROR_NORMAL -ServiceBinary = %12%\usbser.sys -LoadOrderGroup = Base - -[DriverInstall.NTamd64] -include=mdmcpq.inf -CopyFiles=DriverCopyFiles.NTamd64 -AddReg=DriverInstall.NTamd64.AddReg - -[DriverCopyFiles.NTamd64] -usbser.sys,,,0x20 - -[DriverInstall.NTamd64.AddReg] -HKR,,DevLoader,,*ntkern -HKR,,NTMPDriver,,usbser.sys -HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" - -[DriverInstall.NTamd64.Services] -AddService=usbser, 0x00000002, DriverService.NTamd64 - -[DriverService.NTamd64] -DisplayName=%Serial.SvcDesc% -ServiceType=1 -StartType=3 -ErrorControl=1 -ServiceBinary=%12%\usbser.sys - -[Strings] -DIGISOLUTIONS = "dvrptr.de" -DVMODEM_CDC = "DVRPTR CDC" -Serial.SvcDesc = "USB Serial communication driver" diff --git a/WindowsUSB/gmsk.cat b/WindowsUSB/gmsk.cat deleted file mode 100644 index 40c00c7ab6331e2a6dce706c2d25a10713bdc23c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4061 zcmeHKU1%It6h1SZtR~%bt5h^ax7un-%{H7n`#VLJ-O27Wf+p=&WBsAE(I%{|jjgFc zgR~AM&!PzRr(vleL26sYpQx`A(FZGtl|BfyZ;C%u(E1?gckXQBNZpkP@o5?E%$;-Z zJ@?%6o$uar*^ixzwBI__KXs|oxWcUYu}|5L&6$Rw2$c^b?L860GW=|ZfwmJ6&2Ti* zQ5QxH!&>>-JF`1RoU?bnzL(D({(i?Z*LJd_Y?NG@?bLm|BGlb%gVyLN_Kh#BN|F?1 zQEa?3x~3>eMbtbzeN@8ZP!6RGX-*eKDPpwAJByYy#%J)g(U(a1#w1t%n$I*;iQV{> zP*a%W@c5i@Z{F&M!@=p zRY}7NvCGjG*>FS|);U;O1)kJ6*Cdk-8fu#PMxaJxb-wA41<%88JqYmv^}x%pYQ_mfkvetc%l(Qn^X7xNpw zTiYNuO7KonhkX%Vw(GHxhQIzxY-k<|_r&q6-@>Idrf~3=!4$0F5RSR%qwsY!gu*-l zx~##LkaeLahrbpN;g~)h!)dr@X?Zw$G1hg9^K0Qk_06tV4z3y4Jonxa@bHXP5m0Ra znE~q|SRB5_e<2gyMaz$|<$VT_9&5)9JfK|@l@G^`Umun_0M!vg0o<6&Y# z^Y0+IINV^9ZEdaCRvT@tht@q3Id)`U?es@eg`bY~od0bs*%LtO015?m%DKmYO1LV# z3}BX%OO6H?G6_2cn9CY?0bfSSVG;y@1cVzfwFT+Yv-J6EUg&)8<#i9E5bPYixle19%F88B7A6gY&3EAJV``_t;h*$p+{e2aba zB%lo^?1tqV703qSmRttvmjK_$Z>KG^U7wx_e5EcYYy$TMxnMk@-!{U50rPyne`>p3 zhf3p-fz~0Mw!sx -; -; Copyright 2009 MicroWalt Corporation -; All Rights Reserved. -; -; This program or documentation contains proprietary -; confidential information and trade secrets of MicroWalt -; Corporation. Reverse engineering of object code is -; prohibited. Use of copyright notice is precautionary -; and does not imply publication. Any unauthorized use, -; reproduction or transfer of this program is strictly -; prohibited. -; -; RESTRICTED RIGHTS NOTICE -; -; Use, duplication, or disclosure by the U.S. Government -; is subject to restrictions as set forth in subdivision -; (b)(3)(ii) of the Rights in Technical Data and Computer -; Software clause at 252.227-7013. -; -; MicroWalt Corporation -; P O BOX 8 -; 1400AA, BUSSUM, NH -; THE NETHERLANDS -; PH: +31 (35) 7503090 -; FAX: +31 (35) 7503091 -; -[Version] -Signature = "$Windows NT$" -Class = GMSKDeviceClass -ClassGUID = {FEFBC710-4131-4212-B140-6CEC9AF7B35D} -Provider = %ProviderName% -DriverVer = 12/09/2009,1.0.3 -CatalogFile = gmsk.cat - -; Since our device is not a standard USB device, we -; need to define a new class for the device. -[ClassInstall32] -Addreg = GMSKClassReg - -[GMSKClassReg] -HKR,,,0,"GMSK Devices" -HKR,,Icon,,"-20" - -; List the manufactuer, and the models supported. -[Manufacturer] -%ProviderName% = GMSK,NTx86,NTamd64,NTia64 - -[GMSK.NTx86] -"GMSK Modem 300" = GMSK_Install,USB\VID_04D8&PID_0300 -"GMSK Modem 301" = GMSK_Install,USB\VID_04D8&PID_0301 -"GMSK Modem 302" = GMSK_Install,USB\VID_04D8&PID_0302 -"GMSK Modem 303" = GMSK_Install,USB\VID_04D8&PID_0303 -"GMSK Modem 304" = GMSK_Install,USB\VID_04D8&PID_0304 -"GMSK Modem 305" = GMSK_Install,USB\VID_04D8&PID_0305 -"GMSK Modem 306" = GMSK_Install,USB\VID_04D8&PID_0306 -"GMSK Modem 307" = GMSK_Install,USB\VID_04D8&PID_0307 -"GMSK Modem 308" = GMSK_Install,USB\VID_04D8&PID_0308 -"GMSK Modem 309" = GMSK_Install,USB\VID_04D8&PID_0309 -"GMSK Modem 310" = GMSK_Install,USB\VID_04D8&PID_0310 - -[GMSK.NTamd64] -"GMSK Modem 300 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0300 -"GMSK Modem 301 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0301 -"GMSK Modem 302 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0302 -"GMSK Modem 303 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0303 -"GMSK Modem 304 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0304 -"GMSK Modem 305 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0305 -"GMSK Modem 306 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0306 -"GMSK Modem 307 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0307 -"GMSK Modem 308 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0308 -"GMSK Modem 309 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0309 -"GMSK Modem 310 (AMD64)" = GMSK_Install,USB\VID_04D8&PID_0310 - -[GMSK.NTia64] -"GMSK Modem 300 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0300 -"GMSK Modem 301 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0301 -"GMSK Modem 302 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0302 -"GMSK Modem 303 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0303 -"GMSK Modem 304 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0304 -"GMSK Modem 305 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0305 -"GMSK Modem 306 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0306 -"GMSK Modem 307 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0307 -"GMSK Modem 308 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0308 -"GMSK Modem 309 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0309 -"GMSK Modem 310 (IA64)" = GMSK_Install,USB\VID_04D8&PID_0310 - -; =================== Installation =================== -[GMSK_Install] -Include = winusb.inf -Needs = WINUSB.NT - -[GMSK_Install.Services] -Include = winusb.inf -AddService = WinUSB,0x00000002,WinUSB_ServiceInstall - -[GMSK_Install.Wdf] -KmdfService = WINUSB, WinUsb_Install -UmdfServiceOrder = WINUSB - -[GMSK_Install.HW] -AddReg = GMSK_Install_AddReg - -[GMSK_Install_AddReg] -HKR,,DeviceInterfaceGUIDs,0x10000,"{136C76EF-3F4E-4030-A7E3-E1003EF0A715}" - -[GMSK_Install.CoInstallers] -AddReg = CoInstallers_AddReg -CopyFiles = CoInstallers_CopyFiles - -[WinUSB_ServiceInstall] -DisplayName = "WinUSB Devices" -ServiceType = 1 -StartType = 3 -ErrorControl = 1 -ServiceBinary = %12%\WinUSB.sys - -[WinUSB_Install] -KmdfLibraryVersion = 1.7 - -[CoInstallers_AddReg] -HKR,,CoInstallers32,0x00010000,"WinUSBCoInstaller.dll","WdfCoInstaller01007.dll,WdfCoInstaller" - -[CoInstallers_CopyFiles] -WinUSBCoInstaller.dll -WdfCoInstaller01007.dll -;WUDFUpdate_01007.dll - -[DestinationDirs] -CoInstallers_CopyFiles = 11 - -; Select the Source Media. -[SourceDisksNames.x86] -1 = %DISK_NAME%,,,\i386 - -[SourceDisksNames.amd64] -2 = %DISK_NAME%,,,\amd64 - -[SourceDisksNames.ia64] -3 = %DISK_NAME%,,,\ia64 - -[SourceDisksFiles.x86] -WinUSBCoInstaller.dll = 1 -WdfCoInstaller01007.dll = 1 -WUDFUpdate_01007.dll = 1 - -[SourceDisksFiles.amd64] -WinUSBCoInstaller.dll = 2 -WdfCoInstaller01007.dll = 2 -WUDFUpdate_01007.dll = 2 - -[SourceDisksFiles.ia64] -WinUSBCoInstaller.dll = 3 -WdfCoInstaller01007.dll = 3 -WUDFUpdate_01007.dll = 3 - -; Copy Files section -[_CopyFiles_sys] -winusb.sys - -; Destination Directories -[DestinationDirs] -DefaultDestDir = 12 ; %SystemRoot%\System32\DRIVERS -_CopyFiles_sys = 12 - -; Strings -[Strings] -ProviderName = "MicroWalt Corporation" -DISK_NAME = "DUTCH*Star GMSK Install Disk" - -; End of GMSK.INF. diff --git a/WindowsUSB/xDVRPTR-32-64-2.inf b/WindowsUSB/xDVRPTR-32-64-2.inf deleted file mode 100644 index 23062e3..0000000 --- a/WindowsUSB/xDVRPTR-32-64-2.inf +++ /dev/null @@ -1,95 +0,0 @@ -; Windows 2000, XP, Vista and 7 (x32 and x64) setup file for PIPEMSG xDVRPTR CDC Devices -; Copyright (C) 2012 pipeMSG UG -; Creator SAT Hamburg 2012 - -[Version] -Signature="$Windows NT$" -Class=Ports -ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} -Provider=%PIPEMSG% -LayoutFile=layout.inf -DriverVer=12/09/2012,6.0.2404.0 - -[Manufacturer] -%PIPEMSG%=DeviceList, NTamd64 - -[DestinationDirs] -;FakeModemCopyFileSection=12 -;FakeModemCopyFileSection.NTamd64=12 -DefaultDestDir=12 - - -[DeviceList] -xDVRPTR_32=Reader, USB\VID_03EB&PID_2404 -[DeviceList.NTamd64] -xDVRPTR_64=DriverInstall, USB\VID_03EB&PID_2404 - -;------------------------------------------------------------------------------ -; Windows 2000/XP/Vista/7/8 Sections -;------------------------------------------------------------------------------ - -[Reader_Install.NTx86] - -[DestinationDirs] -DefaultDestDir=12 -Reader.NT.Copy=12 - -[Reader.NT] -include=mdmcpq.inf -CopyFiles=Reader.NT.Copy -AddReg=Reader.NT.AddReg - -[Reader.NT.Copy] -usbser.sys - -[Reader.NT.AddReg] -HKR,,DevLoader,,*ntkern -HKR,,NTMPDriver,,usbser.sys -HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" - -[Reader.NT.Services] -AddService = usbser, 0x00000002, Service_Inst - -[Service_Inst] -DisplayName = %Serial.SvcDesc% -ServiceType = 1 ; SERVICE_KERNEL_DRIVER -StartType = 3 ; SERVICE_DEMAND_START -ErrorControl = 1 ; SERVICE_ERROR_NORMAL -ServiceBinary = %12%\usbser.sys -LoadOrderGroup = Base - -;------------------------------------------------------------------------------ -; Windows XP/7/8-64bit Sections -;------------------------------------------------------------------------------ - -[DriverInstall.NTamd64] -include=mdmcpq.inf -CopyFiles=DriverCopyFiles.NTamd64 -AddReg=DriverInstall.NTamd64.AddReg - -[DriverCopyFiles.NTamd64] -usbser.sys,,,0x20 - -[DriverInstall.NTamd64.AddReg] -HKR,,DevLoader,,*ntkern -HKR,,NTMPDriver,,usbser.sys -HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" - -[DriverInstall.NTamd64.Services] -AddService=usbser, 0x00000002, DriverService.NTamd64 - -[DriverService.NTamd64] -DisplayName=%Serial.SvcDesc% -ServiceType=1 -StartType=3 -ErrorControl=1 -ServiceBinary=%12%\usbser.sys -;------------------------------------------------------------------------------ -; String Definitions -;------------------------------------------------------------------------------ - -[Strings] -PIPEMSG = "PIPEMSG.de" -DVMODEM_CDC="xDVRPTR CDC" -DVMODEM_CDC64="xDVRPTR CDC for x64-based systems" -DESCRIPTION="Provides a xDVRPTR CDC COM-Port when connecting an via USB" diff --git a/debian/dstarrepeaterd.dstarrepeaterd@.service b/debian/dstarrepeaterd.dstarrepeaterd@.service index 16f9092..891256b 100644 --- a/debian/dstarrepeaterd.dstarrepeaterd@.service +++ b/debian/dstarrepeaterd.dstarrepeaterd@.service @@ -5,9 +5,9 @@ After=network.target [Service] Environment="WIRINGPI_GPIOMEM=1" User=opendv -ExecStart=/usr/sbin/dstarrepeaterd %i +ExecStart=/usr/bin/dstarrepeaterd /etc/dstarrepeater/%i.ini Restart=on-abort [Install] WantedBy=multi-user.target -DefaultInstance=1 +DefaultInstance=dstarrepeater diff --git a/linux/dstarrepeater.example b/linux/dstarrepeater.example deleted file mode 100644 index fa26b3e..0000000 --- a/linux/dstarrepeater.example +++ /dev/null @@ -1,122 +0,0 @@ -callsign=GB3IN C -gateway= -mode=0 -ack=1 -restriction=0 -rpt1Validation=1 -dtmfBlanking=1 -errorReply=1 -gatewayAddress=127.0.0.1 -gatewayPort=20010 -localAddress=127.0.0.1 -localPort=20011 -networkName= -modemType=DVAP -timeout=180 -ackTime=500 -beaconTime=600 -beaconText=D-Star Repeater -beaconVoice=0 -language=0 -announcementEnabled=0 -announcementTime=500 -announcementRecordRPT1= -announcementRecordRPT2= -announcementDeleteRPT1= -announcementDeleteRPT2= -controlEnabled=0 -controlRPT1= -controlRPT2= -controlShutdown= -controlStartup= -controlStatus1= -controlStatus2= -controlStatus3= -controlStatus4= -controlStatus5= -controlCommand1= -controlCommand1Line= -controlCommand2= -controlCommand2Line= -controlCommand3= -controlCommand3Line= -controlCommand4= -controlCommand4Line= -controlCommand5= -controlCommand5Line= -controlCommand6= -controlCommand6Line= -controlOutput1= -controlOutput2= -controlOutput3= -controlOutput4= -controllerType= -serialConfig=1 -pttInvert=0 -activeHangTime=0 -output1=0 -output2=0 -output3=0 -output4=0 -logging=0 -windowX=-1 -windowY=-1 -dvapPort= -dvapFrequency=145500000 -dvapPower=10 -dvapSquelch=-100 -gmskAddress=768 -dvrptr1Port= -dvrptr1RXInvert=0 -dvrptr1TXInvert=0 -dvrptr1Channel=0 -dvrptr1ModLevel=20 -dvrptr1TXDelay=150 -dvrptr2Connection=0 -dvrptr2USBPort= -dvrptr2Address=127.0.0.1 -dvrptr2Port=0 -dvrptr2TXInvert=0 -dvrptr2ModLevel=20 -dvrptr2TXDelay=150 -dvrptr3Connection=0 -dvrptr3USBPort= -dvrptr3Address=127.0.0.1 -dvrptr3Port=0 -dvrptr3TXInvert=0 -dvrptr3ModLevel=20 -dvrptr3TXDelay=150 -dvmegaPort= -dvmegaVariant=0 -dvmegaRXInvert=0 -dvmegaTXInvert=0 -dvmegaTXDelay=150 -dvmegaRXFrequency=145500000 -dvmegaTXFrequency=145500000 -dvmegaPower=100 -mmdvmPort= -mmdvmRXInvert=0 -mmdvmTXInvert=0 -mmdvmPTTInvert=0 -mmdvmTXDelay=50 -mmdvmRXLevel=100 -mmdvmTXLevel=100 -soundCardRXDevice= -soundCardTXDevice= -soundCardRXInvert=0 -soundCardTXInvert=0 -soundCardRXLevel=1.0000 -soundCardTXLevel=1.0000 -soundCardTXDelay=150 -soundCardTXTail=50 -icomPort= -splitLocalAddress= -splitLocalPort=0 -splitTimeout=0 -mqttHost=127.0.0.1 -mqttPort=1883 -mqttAuth=0 -mqttUsername= -mqttPassword= -mqttKeepalive=60 -mqttName=dstar-repeater diff --git a/schema.json b/schema.json new file mode 100644 index 0000000..0c1ca0f --- /dev/null +++ b/schema.json @@ -0,0 +1,259 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "DStarRepeater MQTT JSON Schema", + "description": "Defines all JSON messages published by DStarRepeater to the MQTT 'json' and 'status' topics. Compatible with Display-Driver and MMDVMHost consumers.", + + "oneOf": [ + { "$ref": "#/$defs/topic_json" }, + { "$ref": "#/$defs/topic_status" } + ], + + "$defs": { + "dstar_callsign": { + "type": "string", + "description": "D-Star callsign, 8 characters, space-padded", + "minLength": 0, + "maxLength": 8 + }, + "dstar_extension": { + "type": "string", + "description": "D-Star callsign extension (short suffix), 4 characters", + "minLength": 0, + "maxLength": 4 + }, + "source": { + "type": "string", + "description": "Origin of the transmission", + "enum": ["rf", "net"] + }, + "action": { + "type": "string", + "description": "D-Star event action type", + "enum": ["start", "end", "lost"] + }, + "rpt_state": { + "type": "string", + "description": "Repeater state machine state", + "enum": ["shutdown", "listening", "valid", "valid_wait", "invalid", "invalid_wait", "timeout", "timeout_wait", "network", "unknown"] + }, + "rx_state": { + "type": "string", + "description": "Receiver state machine state", + "enum": ["listening", "process_data", "process_slow_data", "unknown"] + }, + "ber": { + "type": "number", + "description": "Bit Error Rate as a percentage", + "minimum": 0.0 + }, + "rssi": { + "type": "integer", + "description": "Received signal strength in dBm (DVAP modem only)", + "minimum": -256, + "maximum": -1 + }, + "mmdvm_mode": { + "type": "string", + "description": "MMDVM mode identifier (for Display-Driver compatibility)", + "enum": ["idle", "D-Star"] + }, + + "topic_json": { + "description": "Messages published to {name}/json — event-driven, Display-Driver compatible", + "oneOf": [ + { "$ref": "#/$defs/msg_dstar_start" }, + { "$ref": "#/$defs/msg_dstar_end" }, + { "$ref": "#/$defs/msg_dstar_lost" }, + { "$ref": "#/$defs/msg_mmdvm_idle" }, + { "$ref": "#/$defs/msg_ber" }, + { "$ref": "#/$defs/msg_rssi" }, + { "$ref": "#/$defs/msg_text" } + ] + }, + + "msg_dstar_start": { + "description": "Published when a D-Star transmission begins (RF or network)", + "type": "object", + "properties": { + "D-Star": { + "type": "object", + "properties": { + "action": { "const": "start" }, + "source_cs": { "$ref": "#/$defs/dstar_callsign" }, + "source_ext": { "$ref": "#/$defs/dstar_extension" }, + "destination_cs": { "$ref": "#/$defs/dstar_callsign" }, + "reflector": { "$ref": "#/$defs/dstar_callsign" }, + "source": { "$ref": "#/$defs/source" } + }, + "required": ["action", "source_cs", "source_ext", "destination_cs", "reflector", "source"] + } + }, + "required": ["D-Star"], + "examples": [ + { + "D-Star": { + "action": "start", + "source_cs": "MW0MWZ ", + "source_ext": " ", + "destination_cs": "CQCQCQ ", + "reflector": "GB7XX G", + "source": "rf" + } + } + ] + }, + + "msg_dstar_end": { + "description": "Published when a D-Star transmission ends normally", + "type": "object", + "properties": { + "D-Star": { + "type": "object", + "properties": { + "action": { "const": "end" } + }, + "required": ["action"] + } + }, + "required": ["D-Star"], + "examples": [ + { "D-Star": { "action": "end" } } + ] + }, + + "msg_dstar_lost": { + "description": "Published when a D-Star transmission is lost (watchdog timeout)", + "type": "object", + "properties": { + "D-Star": { + "type": "object", + "properties": { + "action": { "const": "lost" } + }, + "required": ["action"] + } + }, + "required": ["D-Star"], + "examples": [ + { "D-Star": { "action": "lost" } } + ] + }, + + "msg_mmdvm_idle": { + "description": "Published on return to idle (after every end/lost event). Display-Driver compatible.", + "type": "object", + "properties": { + "MMDVM": { + "type": "object", + "properties": { + "mode": { "const": "idle" } + }, + "required": ["mode"] + } + }, + "required": ["MMDVM"], + "examples": [ + { "MMDVM": { "mode": "idle" } } + ] + }, + + "msg_ber": { + "description": "Published once per second during an active RF transmission with the current BER", + "type": "object", + "properties": { + "BER": { + "type": "object", + "properties": { + "mode": { "$ref": "#/$defs/mmdvm_mode" }, + "value": { "$ref": "#/$defs/ber" } + }, + "required": ["mode", "value"] + } + }, + "required": ["BER"], + "examples": [ + { "BER": { "mode": "D-Star", "value": 1.3 } } + ] + }, + + "msg_rssi": { + "description": "Published once per second during an active RF transmission with the DVAP signal strength", + "type": "object", + "properties": { + "RSSI": { + "type": "object", + "properties": { + "mode": { "$ref": "#/$defs/mmdvm_mode" }, + "value": { "$ref": "#/$defs/rssi" } + }, + "required": ["mode", "value"] + } + }, + "required": ["RSSI"], + "examples": [ + { "RSSI": { "mode": "D-Star", "value": -85 } } + ] + }, + + "msg_text": { + "description": "Published when slow-data text is decoded from an RF transmission", + "type": "object", + "properties": { + "Text": { + "type": "object", + "properties": { + "mode": { "$ref": "#/$defs/mmdvm_mode" }, + "value": { "type": "string", "description": "Decoded slow-data text message" } + }, + "required": ["mode", "value"] + } + }, + "required": ["Text"], + "examples": [ + { "Text": { "mode": "D-Star", "value": "Hello from Pi-Star" } } + ] + }, + + "topic_status": { + "description": "Published to {name}/status — polled once per second with full repeater state", + "type": "object", + "properties": { + "myCall1": { "$ref": "#/$defs/dstar_callsign" }, + "myCall2": { "$ref": "#/$defs/dstar_extension" }, + "yourCall": { "$ref": "#/$defs/dstar_callsign" }, + "rptCall1": { "$ref": "#/$defs/dstar_callsign" }, + "rptCall2": { "$ref": "#/$defs/dstar_callsign" }, + "tx": { "type": "boolean", "description": "True when the repeater is transmitting" }, + "rxState": { "$ref": "#/$defs/rx_state" }, + "rptState": { "$ref": "#/$defs/rpt_state" }, + "ber": { "$ref": "#/$defs/ber" }, + "text": { "type": "string", "description": "Current slow-data text (empty when idle)" }, + "status1": { "type": "string", "description": "User-configured status message 1" }, + "status2": { "type": "string", "description": "User-configured status message 2" }, + "status3": { "type": "string", "description": "User-configured status message 3" }, + "status4": { "type": "string", "description": "User-configured status message 4" }, + "status5": { "type": "string", "description": "User-configured status message 5" } + }, + "required": ["myCall1", "myCall2", "yourCall", "rptCall1", "rptCall2", "tx", "rxState", "rptState", "ber", "text", "status1", "status2", "status3", "status4", "status5"], + "examples": [ + { + "myCall1": "", + "myCall2": "", + "yourCall": "", + "rptCall1": "", + "rptCall2": "", + "tx": false, + "rxState": "listening", + "rptState": "listening", + "ber": 0.0, + "text": "", + "status1": "", + "status2": "", + "status3": "", + "status4": "", + "status5": "" + } + ] + } + } +}