merge 3.5-dev into master, this marks 3.5 alpha/beta release; (#31)
* implement inital code to support embedded FNE; * continued implementation of embedded FNE; * implement initial core functionality for handling FNE side of DMRD, P25D and NXDD; * update README.md; * refactor and rename files; clarify that the embedded FNE is not a "routing" FNE, but rather, a "conference bridge" FNE; * more cleanups making the conference bridge FNE implementation clearer; * implement proper code for handling peer inclusion/exclusion; implement proper code for validating a call stream; * add missing comments; * make the GCC compiler happy when compiling on 32-bit ARM; make old RPI_ARM compiler happy; * process parrot flag for talkgroup configuration; * deprecate and remove old tg_acl.dat file infavor of the YAML-based talkgroup rules file for the entire host; deprecate and remove custom Mutex class in favor of std::mutex; expand helper routines for the TalkgroupRulesLookup class (backwards compatibility essentially with TalkgroupIdLookup); fix issue in network core that could cause deactivated TGs to try using talkgroup lookups when talkgroup lookups may be unavailable; * support code for future use; * fixup CMakeLists to fix build warnings for ARM 32-bit platform; * update detection for sendmsg and sendmmsg (handles some platforms that don't define sendmmsg); implement container classes for RTP framing; * update CMakeLists to handle new files appropriately; * initial implementation of FrameQueue to generate FNE RTP frames; * fix incorrect return; * implement a typedef for unique_ptr uint8_t arrays; fix issue where DMR, P25 and NXDN control classes were accepting BaseNetwork instead of Network; refactor how FrameQueue handles returning messages read from the network; refactor BaseNetwork, Network and FNENetwork to use FrameQueue instead of raw network writes; * fix issue with handling network state; fix issue handling global network enable flag; fix buffer pointer cleanup; * add option to CMake to selectively utilize the legacy (non-RTP) network protocol (embedded FNE does not support this and will be disabled for USE_LEGACY_NETWORK; cleanup some error messages when trying to initialize and read the configuration file; fix some minor issues in parsing the new talkgroup rules YAML file; complete conversion of BaseNetwork from raw socket to using the new FrameQueue class; refactor the multi-buffer UDPSocket write function to properly send multiple messages in batch down to the kernel driver (on Linux using sendmmsg); add flag to UDPSocket to indicate whether the socket is "open" or not; correct some RTP formatting issues with the extension header (the payload length is in 32-bit units); * remove remaining native WIN32/WIN64 type support code (it was unmaintained anyway); correct issues with network socket reading; cleanup some error messages;q * finally resolve lingering issue with frame queue having random socket issues; * refactor how BufferVector and sendmmsg UDPSocket write operates; refactor FrameQueue to properly queue messages; * properly queue packets to send in bursts to peers; add missing ACKs; add missing MSTPONG; * fix up formatting; remove unnecessary debug statements; * minor cleanups; * remove unused function; * add fields for function and sub-function; * describe network functions and subfunctions; * reorganize some code; add checking for packet sequence; * roll next sequence properly; * utilize typedef for std::unique_ptr<uint8_t[]>; * fix usage of __UNIQUE_BUFFER to __UNIQUE_UINT8_ARRAY; * clean up macro definitions; * implement sequence counting for the FNE conference bridge; * remove USE_LEGACY_NETWORK support; * use macro expansions for this instead of raw buffer lists; * implement actual transmission of new network func/sub-func behavior; * deprecate use of packet tags for determining operation, instead use RTP FNE header function and subfunction bytes; * fix issue with send talkgroups not setting flag to send talkgroups to peers; fix issue when compiling list of activated or deactivated TGs to send to peers; correct strangeness with the RTP sequence counting; fix talkgroup rule lookup on peer when activating or deactivating TGs; * update copyright dates; * complain if the RTP and FNE headers don't agree on stream ID; * for when you forget to update a comment so things actually make sense when you look at it later...; * remove extraneous check, this is really not necessary since FrameQueue does the size validation; * clarify the description of the "embedded FNE"; * merge changes from https://github.com/CVSoft/dvmhost to place spec specific limits on channel spacing; * refactor how network traffic is handled, we don't handle protocol specific stuff within BaseNetwork anymore and offload that to the specific protocol controller instead; implement support to receive peer traffic and repeat (still need to do master to peer); * convert fatal error about Rx frequency being below base frequency to a warning instead; * fix incorrect redefine of slotNo; * correct ret flag for readDMR, readP25 and readNXDN not being set to true by default (stupid me); correct FNE-mode payload offsets for diagnostic packets; * implement option to selectively enable/disable FNE mode verbose logging information; enhance FNE display of network diagnostic logs; refactor dmr::Control::processNetwork(); refactor RTP packet sequence counting; * fix incorrect variable used for out-of-sequence log message; * fix issues with login RTP stream; specially handle certain control opcodes with a different payload type; continued enhancements to sequence counting; * whoops readd accidentally removed block; * backout previous lastPeerId code; properly ensure RTP packet sequence is repeated for traffic calls; * transmit DVM LC_CALL_TERM TSDU at the end of call or if a call drops (hopefully); * don't reset the packet sequence for a RPTPING; * rename some class variables for clarity and prep for some future changes; * add support for priority/immediate Tx queues, these queues are intended to be used sparingly (and mostly for control data) to transmit priority messages [this is an experimental change and may break the build!]; * fix issue where connecting to a non-existent REST API endpoint would crash with an unhandled ASIO exception; * increase timeout delay for the modem by 4 seconds (the original 4 second delay would be too short for some longer operations); * use the immediate queue to prioritize control data in some situations [this is experimental and may break things!]; * reformat headers; * implement toString() functions on TSBKs, CSBKs and RCCHs for future use to replace fixed strings; cleanup DMRDefines slightly; * fix up and simplify RTP timestamp generation; * reduce reliance on fixed strings, instead rely on toString() functions from TSBK, CSBK, and RCCH to ensure string consistency for logging purpsoes; * refactor how and when releaseGrant is called in some situations; ensure channel grant messages traverse the network; * fix reset of error count between network super-frames; * add development helper file for VS Code to configure debug launch parameters; * update CMakeLists to be more condusive to debugging; * cleanup AUTHORS.md; * implement parrot for the conference bridge FNE; implement appropriate call detection logic for conference bridge FNE and attempt to prevent call collisons for the same destination; * add support for altering softpot levels from calibration mode; * bump copyright information; * reorganize CMakeLists; * remove finalcut from the libraries list (this isn't supposed to be included yet!); * implement initial rework of setup/calibration mode into a TUI; add ENABLE_TUI_SUPPORT CMake option to disable *all* project TUI support; add ENABLE_TUI_SETUP CMake option to disable setup TUI support for dvmhost; * properly update isConnected flag; * fix issue with incorrectly placed control; * part 1 of a fix for #30, the change here would be to allow the VCs to know about the CC to be able to report back to it to release or other wise update grants; * part 2 of the fix for #30, this implements the actual logic for a VC to notify the CC of a grant status change (touch, release) [NOTE: this could have undesirable consequences and this commit is experimental!]; * update README.md; * fix terrible copy and paste job resulting in a poor logic check (SMH); print to log the control channel address and port at startup; use the appropriate REST API for touch (whoops); * remove transmit overlay on shell; add F12 hotkey to trigger transmit; implement support to use F2 and F12 from various configuration/calibration modals; correct some mode strangeness when saving; * implement support to adjust FIFO buffer sizes from the host; * add engineering menu to adjust FIFO buffer sizes from setup; * ensure set button state is set properly for these windows; * fix stupid --tags non-sense in the git hash reporting; * ensure certain error conditions for setup mode are displayed when exiting at initial startup; * more fixes to attempt to satisfy #30 (why is this being so difficult!); correct situation where RESTClient may throw an assert instead of handling an error condition nicely; * for #30 allow release tg grant and touch tg grant to work regardless of dedicated CC setting; * file cleanups; * C++11/14 cleanups, class overrides and pure-virtual implementors /worked/ but were not really correct; * stringify modem command and reason messages (so log errors are easier on the eyes); * late update during a branch merge, to fix some latent issues that were missed;pull/32/head
parent
56e4e7cf37
commit
2b6b0c574d
@ -0,0 +1,131 @@
|
|||||||
|
#
|
||||||
|
# Digital Voice Modem - Host Software Configuration (FNE Conference Bridge Mode)
|
||||||
|
#
|
||||||
|
# @package DVM / Host Software
|
||||||
|
#
|
||||||
|
|
||||||
|
# Flag indicating whether the host will run as a background or foreground task.
|
||||||
|
daemon: true
|
||||||
|
|
||||||
|
#
|
||||||
|
# Logging Configuration
|
||||||
|
# Logging Levels:
|
||||||
|
# 1 - Debug
|
||||||
|
# 2 - Message
|
||||||
|
# 3 - Informational
|
||||||
|
# 4 - Warning
|
||||||
|
# 5 - Error
|
||||||
|
# 6 - Fatal
|
||||||
|
#
|
||||||
|
log:
|
||||||
|
# Console display logging level (used when in foreground).
|
||||||
|
displayLevel: 1
|
||||||
|
# File logging level.
|
||||||
|
fileLevel: 1
|
||||||
|
# Full path for the directory to store the log files.
|
||||||
|
filePath: .
|
||||||
|
# Full path for the directory to store the activity log files.
|
||||||
|
activityFilePath: .
|
||||||
|
# Log filename prefix.
|
||||||
|
fileRoot: DVM
|
||||||
|
|
||||||
|
#
|
||||||
|
# Master
|
||||||
|
#
|
||||||
|
master:
|
||||||
|
# Network Peer ID
|
||||||
|
peerId: 9000100
|
||||||
|
# Hostname/IP address to listen on (blank for all).
|
||||||
|
address: 0.0.0.0
|
||||||
|
# Port number to listen on.
|
||||||
|
port: 62031
|
||||||
|
# FNE access password.
|
||||||
|
password: RPT1234
|
||||||
|
# Flag indicating whether or not verbose logging is enabled.
|
||||||
|
verbose: true
|
||||||
|
# Flag indicating whether or not verbose debug logging is enabled.
|
||||||
|
debug: false
|
||||||
|
|
||||||
|
# Flag indicating whether or not DMR traffic will be passed.
|
||||||
|
allowDMRTraffic: true
|
||||||
|
# Flag indicating whether or not P25 traffic will be passed.
|
||||||
|
allowP25Traffic: true
|
||||||
|
# Flag indicating whether or not NXDN traffic will be passed.
|
||||||
|
allowNXDNTraffic: true
|
||||||
|
|
||||||
|
# Delay from when a call on a parrot TG ends to when the playback starts (in milliseconds).
|
||||||
|
parrotDelay: 2000
|
||||||
|
|
||||||
|
#
|
||||||
|
# Talkgroup Rules Configuration
|
||||||
|
#
|
||||||
|
talkgroup_rules:
|
||||||
|
# Full path to the talkgroup rules file.
|
||||||
|
file: talkgroup_rules.yml
|
||||||
|
# Amount of time between updates of talkgroup rules file. (minutes)
|
||||||
|
time: 30
|
||||||
|
|
||||||
|
#
|
||||||
|
# Peers
|
||||||
|
#
|
||||||
|
peers:
|
||||||
|
- name: PARROT
|
||||||
|
# Flag indicating whether or not the peer is enabled.
|
||||||
|
enabled: true
|
||||||
|
# Hostname/IP address to listen on (blank for all).
|
||||||
|
address: 127.0.0.1
|
||||||
|
# Port number to listen on.
|
||||||
|
port: 32091
|
||||||
|
# Hostname/IP address of the FNE master to connect to.
|
||||||
|
masterAddress: 127.0.0.1
|
||||||
|
# Port number of the FNE master to connect to.
|
||||||
|
masterPort: 32090
|
||||||
|
# FNE access password.
|
||||||
|
password: RPT1234
|
||||||
|
# Textual identity of this peer.
|
||||||
|
identity: PARROT
|
||||||
|
# Network Peer ID
|
||||||
|
peerId: 9000990
|
||||||
|
|
||||||
|
#
|
||||||
|
rxFrequency: 0
|
||||||
|
#
|
||||||
|
txFrequency: 0
|
||||||
|
# Latitude.
|
||||||
|
latitude: 0.0
|
||||||
|
# Longitude.
|
||||||
|
longitude: 0.0
|
||||||
|
# Textual location for this host.
|
||||||
|
location: Anywhere, USA
|
||||||
|
|
||||||
|
# Flag indicating whether or not verbose debug logging is enabled.
|
||||||
|
debug: false
|
||||||
|
|
||||||
|
#
|
||||||
|
# System Configuration
|
||||||
|
#
|
||||||
|
system:
|
||||||
|
# Time in seconds between pings to peers.
|
||||||
|
pingTime: 5
|
||||||
|
# Maximum number of missable pings before a peer is considered disconnected.
|
||||||
|
maxMissedPings: 5
|
||||||
|
|
||||||
|
# Time in minutes between updates of the talkgroup rules.
|
||||||
|
tgRuleUpdateTime: 10
|
||||||
|
|
||||||
|
# Flag indicating the TGID information for this master will be sent to its peers.
|
||||||
|
sendTalkgroups: true
|
||||||
|
|
||||||
|
# Flag indicating whether or not the host activity log will be sent to the network.
|
||||||
|
allowActivityTransfer: true
|
||||||
|
# Flag indicating whether or not the host diagnostic log will be sent to the network.
|
||||||
|
allowDiagnosticTransfer: true
|
||||||
|
|
||||||
|
#
|
||||||
|
# Radio ID ACL Configuration
|
||||||
|
#
|
||||||
|
radio_id:
|
||||||
|
# Full path to the identity table file.
|
||||||
|
file: rid_acl.dat
|
||||||
|
# Amount of time between updates of identity table file. (minutes)
|
||||||
|
time: 2
|
||||||
@ -0,0 +1,131 @@
|
|||||||
|
#
|
||||||
|
# Digital Voice Modem - Talkgroup Rules
|
||||||
|
#
|
||||||
|
# @package DVM / Host Software
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Talkgroup Rules
|
||||||
|
#
|
||||||
|
groupVoice:
|
||||||
|
# Textual name of the talkgroup.
|
||||||
|
- name: Talkgroup 1
|
||||||
|
#
|
||||||
|
# Talkgroup Configuration
|
||||||
|
#
|
||||||
|
config:
|
||||||
|
# Flag indicating whether this talkgroup is active or not.
|
||||||
|
active: true
|
||||||
|
# Flag indicating whether this talkgroup requires affiliations to repeat traffic.
|
||||||
|
affiliated: false
|
||||||
|
|
||||||
|
# List of peer IDs included for this talkgroup (peers listed here will be selected for traffic).
|
||||||
|
inclusion: []
|
||||||
|
# List of peer IDs excluded for this talkgroup (peers listed here will be ignored for traffic).
|
||||||
|
exclusion: []
|
||||||
|
#
|
||||||
|
# Source Configuration
|
||||||
|
#
|
||||||
|
source:
|
||||||
|
# Numerical talkgroup ID number.
|
||||||
|
tgid: 1
|
||||||
|
# DMR slot number.
|
||||||
|
slot: 1
|
||||||
|
|
||||||
|
# Textual name of the talkgroup.
|
||||||
|
- name: Parrot
|
||||||
|
#
|
||||||
|
# Talkgroup Configuration
|
||||||
|
#
|
||||||
|
config:
|
||||||
|
# Flag indicating whether this talkgroup is active or not.
|
||||||
|
active: true
|
||||||
|
# Flag indicating whether this talkgroup requires affiliations to repeat traffic.
|
||||||
|
affiliated: false
|
||||||
|
# Flag indicating whether or not this talkgroup is a parrot talkgroup.
|
||||||
|
parrot: true
|
||||||
|
|
||||||
|
# List of peer IDs included for this talkgroup (peers listed here will be selected for traffic).
|
||||||
|
inclusion: []
|
||||||
|
# List of peer IDs excluded for this talkgroup (peers listed here will be ignored for traffic).
|
||||||
|
exclusion: []
|
||||||
|
#
|
||||||
|
# Source Configuration
|
||||||
|
#
|
||||||
|
source:
|
||||||
|
# Numerical talkgroup ID number.
|
||||||
|
tgid: 9990
|
||||||
|
# DMR slot number.
|
||||||
|
slot: 1
|
||||||
|
|
||||||
|
# Textual name of the talkgroup.
|
||||||
|
- name: System Wide P25
|
||||||
|
#
|
||||||
|
# Talkgroup Configuration
|
||||||
|
#
|
||||||
|
config:
|
||||||
|
# Flag indicating whether this talkgroup is active or not.
|
||||||
|
active: true
|
||||||
|
# Flag indicating whether this talkgroup requires affiliations to repeat traffic.
|
||||||
|
affiliated: false
|
||||||
|
|
||||||
|
# List of peer IDs included for this talkgroup (peers listed here will be selected for traffic).
|
||||||
|
inclusion: []
|
||||||
|
# List of peer IDs excluded for this talkgroup (peers listed here will be ignored for traffic).
|
||||||
|
exclusion: []
|
||||||
|
#
|
||||||
|
# Source Configuration
|
||||||
|
#
|
||||||
|
source:
|
||||||
|
# Numerical talkgroup ID number.
|
||||||
|
tgid: 65535
|
||||||
|
# DMR slot number.
|
||||||
|
slot: 1
|
||||||
|
|
||||||
|
# Textual name of the talkgroup.
|
||||||
|
- name: System Wide DMR TS1
|
||||||
|
#
|
||||||
|
# Talkgroup Configuration
|
||||||
|
#
|
||||||
|
config:
|
||||||
|
# Flag indicating whether this talkgroup is active or not.
|
||||||
|
active: true
|
||||||
|
# Flag indicating whether this talkgroup requires affiliations to repeat traffic.
|
||||||
|
affiliated: false
|
||||||
|
|
||||||
|
# List of peer IDs included for this talkgroup (peers listed here will be selected for traffic).
|
||||||
|
inclusion: []
|
||||||
|
# List of peer IDs excluded for this talkgroup (peers listed here will be ignored for traffic).
|
||||||
|
exclusion: []
|
||||||
|
#
|
||||||
|
# Source Configuration
|
||||||
|
#
|
||||||
|
source:
|
||||||
|
# Numerical talkgroup ID number.
|
||||||
|
tgid: 16777215
|
||||||
|
# DMR slot number.
|
||||||
|
slot: 1
|
||||||
|
|
||||||
|
# Textual name of the talkgroup.
|
||||||
|
- name: System Wide DMR TS2
|
||||||
|
#
|
||||||
|
# Talkgroup Configuration
|
||||||
|
#
|
||||||
|
config:
|
||||||
|
# Flag indicating whether this talkgroup is active or not.
|
||||||
|
active: true
|
||||||
|
# Flag indicating whether this talkgroup requires affiliations to repeat traffic.
|
||||||
|
affiliated: false
|
||||||
|
|
||||||
|
# List of peer IDs included for this talkgroup (peers listed here will be selected for traffic).
|
||||||
|
inclusion: []
|
||||||
|
# List of peer IDs excluded for this talkgroup (peers listed here will be ignored for traffic).
|
||||||
|
exclusion: []
|
||||||
|
#
|
||||||
|
# Source Configuration
|
||||||
|
#
|
||||||
|
source:
|
||||||
|
# Numerical talkgroup ID number.
|
||||||
|
tgid: 16777215
|
||||||
|
# DMR slot number.
|
||||||
|
slot: 2
|
||||||
@ -1,6 +0,0 @@
|
|||||||
#
|
|
||||||
# This file sets the valid Talkgroup IDs allowed on a repeater.
|
|
||||||
#
|
|
||||||
# TGID,Enabled (1 = Enabled / 0 = Disabled),Slot (0 = Both, 1 = Slot 1, 2 = Slot 2),<newline>
|
|
||||||
16777215,1,0,
|
|
||||||
65535,1,0,
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||||
|
// Use hover for the description of the existing attributes
|
||||||
|
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||||
|
"name": "Debug",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
|
"program": "${workspaceFolder}/build/dvmhost",
|
||||||
|
"args": ["-c", "./config.yml", "-f"],
|
||||||
|
"cwd": "${workspaceFolder}/build",
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"logging": {
|
||||||
|
"moduleLoad": true,
|
||||||
|
"trace": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,360 @@
|
|||||||
|
#/**
|
||||||
|
#* Digital Voice Modem - Host Software
|
||||||
|
#* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
#* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
#*
|
||||||
|
#* @package DVM / Host Software
|
||||||
|
#*
|
||||||
|
#*/
|
||||||
|
#/*
|
||||||
|
#* Copyright (C) 2022 by Bryan Biedenkapp N2PLL
|
||||||
|
#* Copyright (C) 2022 by Natalie Moore <https://github.com/jelimoore>
|
||||||
|
#*
|
||||||
|
#* This program is free software; 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.
|
||||||
|
#*/
|
||||||
|
#
|
||||||
|
## dvmhost source/header files
|
||||||
|
#
|
||||||
|
file(GLOB dvmhost_SRC
|
||||||
|
# DMR module
|
||||||
|
"src/dmr/*.h"
|
||||||
|
"src/dmr/*.cpp"
|
||||||
|
"src/dmr/acl/*.h"
|
||||||
|
"src/dmr/acl/*.cpp"
|
||||||
|
"src/dmr/data/*.h"
|
||||||
|
"src/dmr/data/*.cpp"
|
||||||
|
"src/dmr/edac/*.h"
|
||||||
|
"src/dmr/edac/*.cpp"
|
||||||
|
"src/dmr/lc/*.h"
|
||||||
|
"src/dmr/lc/*.cpp"
|
||||||
|
"src/dmr/lc/csbk/*.h"
|
||||||
|
"src/dmr/lc/csbk/*.cpp"
|
||||||
|
"src/dmr/lookups/*.h"
|
||||||
|
"src/dmr/lookups/*.cpp"
|
||||||
|
"src/dmr/packet*.h"
|
||||||
|
"src/dmr/packet/*.cpp"
|
||||||
|
|
||||||
|
# P25 module
|
||||||
|
"src/p25/*.h"
|
||||||
|
"src/p25/*.cpp"
|
||||||
|
"src/p25/acl/*.h"
|
||||||
|
"src/p25/acl/*.cpp"
|
||||||
|
"src/p25/data/*.h"
|
||||||
|
"src/p25/data/*.cpp"
|
||||||
|
"src/p25/dfsi/*.h"
|
||||||
|
"src/p25/dfsi/*.cpp"
|
||||||
|
"src/p25/dfsi/packet/*.h"
|
||||||
|
"src/p25/dfsi/packet/*.cpp"
|
||||||
|
"src/p25/edac/*.h"
|
||||||
|
"src/p25/edac/*.cpp"
|
||||||
|
"src/p25/lc/*.h"
|
||||||
|
"src/p25/lc/*.cpp"
|
||||||
|
"src/p25/lc/tdulc/*.h"
|
||||||
|
"src/p25/lc/tdulc/*.cpp"
|
||||||
|
"src/p25/lc/tsbk/*.h"
|
||||||
|
"src/p25/lc/tsbk/*.cpp"
|
||||||
|
"src/p25/lookups/*.h"
|
||||||
|
"src/p25/lookups/*.cpp"
|
||||||
|
"src/p25/packet/*.h"
|
||||||
|
"src/p25/packet/*.cpp"
|
||||||
|
|
||||||
|
# NXDN module
|
||||||
|
"src/nxdn/*.h"
|
||||||
|
"src/nxdn/*.cpp"
|
||||||
|
"src/nxdn/acl/*.h"
|
||||||
|
"src/nxdn/acl/*.cpp"
|
||||||
|
"src/nxdn/channel/*.h"
|
||||||
|
"src/nxdn/channel/*.cpp"
|
||||||
|
"src/nxdn/edac/*.h"
|
||||||
|
"src/nxdn/edac/*.cpp"
|
||||||
|
"src/nxdn/lc/*.h"
|
||||||
|
"src/nxdn/lc/*.cpp"
|
||||||
|
"src/nxdn/lc/rcch/*.h"
|
||||||
|
"src/nxdn/lc/rcch/*.cpp"
|
||||||
|
"src/nxdn/packet/*.h"
|
||||||
|
"src/nxdn/packet/*.cpp"
|
||||||
|
|
||||||
|
# Core
|
||||||
|
"src/edac/*.h"
|
||||||
|
"src/edac/*.cpp"
|
||||||
|
"src/edac/rs/*.h"
|
||||||
|
"src/host/*.h"
|
||||||
|
"src/host/*.cpp"
|
||||||
|
"src/host/calibrate/*.h"
|
||||||
|
"src/host/calibrate/*.cpp"
|
||||||
|
"src/host/setup/*.h"
|
||||||
|
"src/host/setup/*.cpp"
|
||||||
|
"src/host/fne/*.h"
|
||||||
|
"src/host/fne/*.cpp"
|
||||||
|
"src/lookups/*.h"
|
||||||
|
"src/lookups/*.cpp"
|
||||||
|
"src/modem/*.h"
|
||||||
|
"src/modem/*.cpp"
|
||||||
|
"src/modem/port/*.h"
|
||||||
|
"src/modem/port/*.cpp"
|
||||||
|
"src/network/*.h"
|
||||||
|
"src/network/*.cpp"
|
||||||
|
"src/network/fne/*.h"
|
||||||
|
"src/network/fne/*.cpp"
|
||||||
|
"src/network/json/*.h"
|
||||||
|
"src/network/rest/*.h"
|
||||||
|
"src/network/rest/*.cpp"
|
||||||
|
"src/network/rest/http/*.h"
|
||||||
|
"src/network/rest/http/*.cpp"
|
||||||
|
"src/remote/RESTClient.cpp"
|
||||||
|
"src/remote/RESTClient.h"
|
||||||
|
"src/yaml/*.h"
|
||||||
|
"src/yaml/*.cpp"
|
||||||
|
"src/*.h"
|
||||||
|
"src/*.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
## dvmcmd source/header files
|
||||||
|
#
|
||||||
|
file(GLOB dvmcmd_SRC
|
||||||
|
"src/network/UDPSocket.h"
|
||||||
|
"src/network/UDPSocket.cpp"
|
||||||
|
"src/network/RESTDefines.h"
|
||||||
|
"src/network/json/*.h"
|
||||||
|
"src/network/rest/*.h"
|
||||||
|
"src/network/rest/*.cpp"
|
||||||
|
"src/network/rest/http/*.h"
|
||||||
|
"src/network/rest/http/*.cpp"
|
||||||
|
"src/remote/*.h"
|
||||||
|
"src/remote/*.cpp"
|
||||||
|
"src/edac/SHA256.h"
|
||||||
|
"src/edac/SHA256.cpp"
|
||||||
|
"src/Defines.h"
|
||||||
|
"src/Thread.h"
|
||||||
|
"src/Thread.cpp"
|
||||||
|
"src/Log.h"
|
||||||
|
"src/Log.cpp"
|
||||||
|
"src/Utils.h"
|
||||||
|
"src/Utils.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Digital mode options and other compilation features
|
||||||
|
option(ENABLE_DMR "Enable DMR Digtial Mode" on)
|
||||||
|
if (ENABLE_DMR)
|
||||||
|
add_definitions(-DENABLE_DMR)
|
||||||
|
message(CHECK_START "DMR Digital Mode - enabled")
|
||||||
|
else ()
|
||||||
|
message(CHECK_START "DMR Digital Mode - disabled")
|
||||||
|
endif (ENABLE_DMR)
|
||||||
|
|
||||||
|
option(ENABLE_P25 "Enable P25 Digital Mode" on)
|
||||||
|
if (ENABLE_P25)
|
||||||
|
add_definitions(-DENABLE_P25)
|
||||||
|
message(CHECK_START "P25 Digital Mode - enabled")
|
||||||
|
else ()
|
||||||
|
message(CHECK_START "P25 Digital Mode - disabled")
|
||||||
|
endif (ENABLE_P25)
|
||||||
|
|
||||||
|
option(ENABLE_NXDN "Enable NXDN Digital Mode" on)
|
||||||
|
if (ENABLE_NXDN)
|
||||||
|
add_definitions(-DENABLE_NXDN)
|
||||||
|
message(CHECK_START "NXDN Digital Mode - enabled")
|
||||||
|
else ()
|
||||||
|
message(CHECK_START "NXDN Digital Mode - disabled")
|
||||||
|
endif (ENABLE_NXDN)
|
||||||
|
|
||||||
|
option(ENABLE_DFSI_SUPPORT "Enable P25 DFSI Transport Support" off)
|
||||||
|
if (ENABLE_DFSI_SUPPORT)
|
||||||
|
add_definitions(-DENABLE_DFSI_SUPPORT)
|
||||||
|
message(CHECK_START "P25 DFSI Support - enabled")
|
||||||
|
endif (ENABLE_DFSI_SUPPORT)
|
||||||
|
|
||||||
|
if (ENABLE_TUI_SUPPORT)
|
||||||
|
option(ENABLE_SETUP_TUI "Enable interactive setup TUI" on)
|
||||||
|
if (ENABLE_SETUP_TUI)
|
||||||
|
add_definitions(-DENABLE_SETUP_TUI)
|
||||||
|
message(CHECK_START "Interactive Setup TUI - enabled")
|
||||||
|
endif (ENABLE_SETUP_TUI)
|
||||||
|
else()
|
||||||
|
set(ENABLE_SETUP_TUI off)
|
||||||
|
endif (ENABLE_TUI_SUPPORT)
|
||||||
|
|
||||||
|
# Debug compilation features/options (these should not be enabled for production!)
|
||||||
|
option(DEBUG_DMR_PDU_DATA "" off)
|
||||||
|
option(DEBUG_CRC "" off)
|
||||||
|
option(DEBUG_RS "" off)
|
||||||
|
option(DEBUG_MODEM_CAL "" off)
|
||||||
|
option(DEBUG_MODEM "" off)
|
||||||
|
option(DEBUG_NXDN_FACCH1 "" off)
|
||||||
|
option(DEBUG_NXDN_SACCH "" off)
|
||||||
|
option(DEBUG_NXDN_UDCH "" off)
|
||||||
|
option(DEBUG_NXDN_LICH "" off)
|
||||||
|
option(DEBUG_NXDN_CAC "" off)
|
||||||
|
option(DEBUG_P25_PDU_DATA "" off)
|
||||||
|
option(DEBUG_P25_HDU "" off)
|
||||||
|
option(DEBUG_P25_LDU1 "" off)
|
||||||
|
option(DEBUG_P25_LDU2 "" off)
|
||||||
|
option(DEBUG_P25_TDULC "" off)
|
||||||
|
option(DEBUG_P25_TSBK "" off)
|
||||||
|
option(FORCE_TSBK_CRC_WARN "" off)
|
||||||
|
option(DEBUG_P25_DFSI "" off)
|
||||||
|
option(DEBUG_RINGBUFFER "" off)
|
||||||
|
option(DEBUG_HTTP_PAYLOAD "" off)
|
||||||
|
option(DEBUG_TRELLIS "" off)
|
||||||
|
|
||||||
|
if (DEBUG_DMR_PDU_DATA)
|
||||||
|
add_definitions(-DDEBUG_DMR_PDU_DATA)
|
||||||
|
endif (DEBUG_DMR_PDU_DATA)
|
||||||
|
if (DEBUG_CRC_ADD)
|
||||||
|
add_definitions(-DDEBUG_CRC_ADD)
|
||||||
|
endif (DEBUG_CRC_ADD)
|
||||||
|
if (DEBUG_CRC_CHECK)
|
||||||
|
add_definitions(-DDEBUG_CRC_CHECK)
|
||||||
|
endif (DEBUG_CRC_CHECK)
|
||||||
|
if (DEBUG_RS)
|
||||||
|
add_definitions(-DDEBUG_RS)
|
||||||
|
endif (DEBUG_RS)
|
||||||
|
if (DEBUG_MODEM_CAL)
|
||||||
|
add_definitions(-DDEBUG_MODEM_CAL)
|
||||||
|
endif (DEBUG_MODEM_CAL)
|
||||||
|
if (DEBUG_MODEM)
|
||||||
|
add_definitions(-DDEBUG_MODEM)
|
||||||
|
endif (DEBUG_MODEM)
|
||||||
|
if (DEBUG_NXDN_FACCH1)
|
||||||
|
add_definitions(-DDEBUG_NXDN_FACCH1)
|
||||||
|
endif (DEBUG_NXDN_FACCH1)
|
||||||
|
if (DEBUG_NXDN_SACCH)
|
||||||
|
add_definitions(-DDEBUG_NXDN_SACCH)
|
||||||
|
endif (DEBUG_NXDN_SACCH)
|
||||||
|
if (DEBUG_NXDN_UDCH)
|
||||||
|
add_definitions(-DDEBUG_NXDN_UDCH)
|
||||||
|
endif (DEBUG_NXDN_UDCH)
|
||||||
|
if (DEBUG_NXDN_LICH)
|
||||||
|
add_definitions(-DDEBUG_NXDN_LICH)
|
||||||
|
endif (DEBUG_NXDN_LICH)
|
||||||
|
if (DEBUG_NXDN_CAC)
|
||||||
|
add_definitions(-DDEBUG_NXDN_CAC)
|
||||||
|
endif (DEBUG_NXDN_CAC)
|
||||||
|
if (DEBUG_P25_PDU_DATA)
|
||||||
|
add_definitions(-DDEBUG_P25_PDU_DATA)
|
||||||
|
endif (DEBUG_P25_PDU_DATA)
|
||||||
|
if (DEBUG_P25_HDU)
|
||||||
|
add_definitions(-DDEBUG_P25_HDU)
|
||||||
|
endif (DEBUG_P25_HDU)
|
||||||
|
if (DEBUG_P25_LDU1)
|
||||||
|
add_definitions(-DDEBUG_P25_LDU1)
|
||||||
|
endif (DEBUG_P25_LDU1)
|
||||||
|
if (DEBUG_P25_LDU2)
|
||||||
|
add_definitions(-DDEBUG_P25_LDU2)
|
||||||
|
endif (DEBUG_P25_LDU2)
|
||||||
|
if (DEBUG_P25_TDULC)
|
||||||
|
add_definitions(-DDEBUG_P25_TDULC)
|
||||||
|
endif (DEBUG_P25_TDULC)
|
||||||
|
if (DEBUG_P25_TSBK)
|
||||||
|
add_definitions(-DDEBUG_P25_TSBK)
|
||||||
|
endif (DEBUG_P25_TSBK)
|
||||||
|
if (FORCE_TSBK_CRC_WARN)
|
||||||
|
add_definitions(-DFORCE_TSBK_CRC_WARN)
|
||||||
|
endif (FORCE_TSBK_CRC_WARN)
|
||||||
|
if (DEBUG_P25_DFSI)
|
||||||
|
add_definitions(-DDEBUG_P25_DFSI)
|
||||||
|
endif (DEBUG_P25_DFSI)
|
||||||
|
if (DEBUG_RINGBUFFER)
|
||||||
|
add_definitions(-DDEBUG_RINGBUFFER)
|
||||||
|
endif (DEBUG_RINGBUFFER)
|
||||||
|
if (DEBUG_HTTP_PAYLOAD)
|
||||||
|
add_definitions(-DDEBUG_HTTP_PAYLOAD)
|
||||||
|
endif (DEBUG_HTTP_PAYLOAD)
|
||||||
|
if (DEBUG_TRELLIS)
|
||||||
|
add_definitions(-DDEBUG_TRELLIS)
|
||||||
|
endif (DEBUG_TRELLIS)
|
||||||
|
|
||||||
|
#
|
||||||
|
## dvmhost project
|
||||||
|
#
|
||||||
|
project(dvmhost)
|
||||||
|
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
# add ASIO
|
||||||
|
add_library(asio::asio INTERFACE IMPORTED)
|
||||||
|
target_include_directories(asio::asio INTERFACE ${ASIO_INCLUDE_DIR})
|
||||||
|
target_compile_definitions(asio::asio INTERFACE "ASIO_STANDALONE")
|
||||||
|
target_link_libraries(asio::asio INTERFACE Threads::Threads)
|
||||||
|
|
||||||
|
if (ENABLE_SETUP_TUI)
|
||||||
|
# add finalcut
|
||||||
|
target_include_directories(finalcut INTERFACE ${FINALCUT_INCLUDE_DIR})
|
||||||
|
endif (ENABLE_SETUP_TUI)
|
||||||
|
|
||||||
|
# Check if platform-specific functions exist
|
||||||
|
include(CheckCXXSymbolExists)
|
||||||
|
check_cxx_symbol_exists(sendmsg sys/socket.h HAVE_SENDMSG)
|
||||||
|
check_cxx_symbol_exists(sendmmsg sys/socket.h HAVE_SENDMMSG)
|
||||||
|
|
||||||
|
if (HAVE_SENDMSG)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_SENDMSG=1")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_SENDMSG=1")
|
||||||
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DHAVE_SENDMSG=1")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DHAVE_SENDMSG=1")
|
||||||
|
endif (HAVE_SENDMSG)
|
||||||
|
if (HAVE_SENDMMSG)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_SENDMMSG=1")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_SENDMMSG=1")
|
||||||
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DHAVE_SENDMMSG=1")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DHAVE_SENDMMSG=1")
|
||||||
|
endif (HAVE_SENDMMSG)
|
||||||
|
|
||||||
|
add_executable(dvmhost ${dvmhost_SRC})
|
||||||
|
target_include_directories(dvmhost PRIVATE src)
|
||||||
|
if (ENABLE_SETUP_TUI)
|
||||||
|
target_link_libraries(dvmhost PRIVATE asio::asio finalcut Threads::Threads util)
|
||||||
|
else()
|
||||||
|
target_link_libraries(dvmhost PRIVATE asio::asio Threads::Threads util)
|
||||||
|
endif (ENABLE_SETUP_TUI)
|
||||||
|
|
||||||
|
set(CPACK_SET_DESTDIR true)
|
||||||
|
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/local")
|
||||||
|
|
||||||
|
set(CPACK_GENERATOR "DEB")
|
||||||
|
set(CPACK_PACKAGE_NAME "dvmhost")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_NAME "dvmhost")
|
||||||
|
|
||||||
|
set(CPACK_PACKAGE_VENDOR "DVMProject")
|
||||||
|
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "The DVM Host software provides the host computer implementation of a mixed-mode DMR, P25 and/or NXDN or dedicated-mode DMR, P25 or NXDN repeater system that talks to the actual modem hardware. The host software; is the portion of a complete Over-The-Air modem implementation that performs the data processing, decision making and FEC correction for a digital repeater.")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "DVMProject Authors")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_VERSION "3.0.0")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_RELEASE "0")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/dvmproject")
|
||||||
|
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/debian/postinst;${CMAKE_CURRENT_SOURCE_DIR}/debian/postrm")
|
||||||
|
|
||||||
|
set(CPACK_DEBIAN_FILE_NAME ${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}-${CPACK_DEBIAN_PACKAGE_RELEASE}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb)
|
||||||
|
|
||||||
|
include(CPack)
|
||||||
|
|
||||||
|
#
|
||||||
|
## dvmcmd project
|
||||||
|
#
|
||||||
|
project(dvmcmd)
|
||||||
|
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
# add ASIO
|
||||||
|
target_include_directories(asio::asio INTERFACE ${ASIO_INCLUDE_DIR})
|
||||||
|
target_compile_definitions(asio::asio INTERFACE "ASIO_STANDALONE")
|
||||||
|
target_link_libraries(asio::asio INTERFACE Threads::Threads)
|
||||||
|
|
||||||
|
add_executable(dvmcmd ${dvmcmd_SRC})
|
||||||
|
target_link_libraries(dvmcmd PRIVATE asio::asio Threads::Threads)
|
||||||
|
target_include_directories(dvmcmd PRIVATE src)
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,470 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; 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 "Defines.h"
|
||||||
|
#include "network/fne/TagDMRData.h"
|
||||||
|
#include "network/fne/TagP25Data.h"
|
||||||
|
#include "network/fne/TagNXDNData.h"
|
||||||
|
#include "network/UDPSocket.h"
|
||||||
|
#include "host/fne/HostFNE.h"
|
||||||
|
#include "HostMain.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "StopWatch.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
using namespace network;
|
||||||
|
using namespace lookups;
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define IDLE_WARMUP_MS 5U
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the HostFNE class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="confFile">Full-path to the configuration file.</param>
|
||||||
|
HostFNE::HostFNE(const std::string& confFile) :
|
||||||
|
m_confFile(confFile),
|
||||||
|
m_conf(),
|
||||||
|
m_network(nullptr),
|
||||||
|
m_dmrEnabled(false),
|
||||||
|
m_p25Enabled(false),
|
||||||
|
m_nxdnEnabled(false),
|
||||||
|
m_ridLookup(nullptr),
|
||||||
|
m_tidLookup(nullptr),
|
||||||
|
m_peerNetworks(),
|
||||||
|
m_pingTime(5U),
|
||||||
|
m_maxMissedPings(5U),
|
||||||
|
m_updateLookupTime(10U),
|
||||||
|
m_allowActivityTransfer(false),
|
||||||
|
m_allowDiagnosticTransfer(false)
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes a instance of the HostFNE class.
|
||||||
|
/// </summary>
|
||||||
|
HostFNE::~HostFNE()
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes the main FNE processing loop.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Zero if successful, otherwise error occurred.</returns>
|
||||||
|
int HostFNE::run()
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
try {
|
||||||
|
ret = yaml::Parse(m_conf, m_confFile.c_str());
|
||||||
|
if (!ret) {
|
||||||
|
::fatal("cannot read the configuration file, %s\n", m_confFile.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (yaml::OperationException const& e) {
|
||||||
|
::fatal("cannot read the configuration file - %s (%s)", m_confFile.c_str(), e.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_daemon = m_conf["daemon"].as<bool>(false);
|
||||||
|
if (m_daemon && g_foreground)
|
||||||
|
m_daemon = false;
|
||||||
|
|
||||||
|
// initialize system logging
|
||||||
|
yaml::Node logConf = m_conf["log"];
|
||||||
|
ret = ::LogInitialise(logConf["filePath"].as<std::string>(), logConf["fileRoot"].as<std::string>(),
|
||||||
|
logConf["fileLevel"].as<uint32_t>(0U), logConf["displayLevel"].as<uint32_t>(0U));
|
||||||
|
if (!ret) {
|
||||||
|
::fatal("unable to open the log file\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ::ActivityLogInitialise(logConf["activityFilePath"].as<std::string>(), logConf["fileRoot"].as<std::string>());
|
||||||
|
if (!ret) {
|
||||||
|
::fatal("unable to open the activity log file\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle POSIX process forking
|
||||||
|
if (m_daemon) {
|
||||||
|
// create new process
|
||||||
|
pid_t pid = ::fork();
|
||||||
|
if (pid == -1) {
|
||||||
|
::fprintf(stderr, "%s: Couldn't fork() , exiting\n", g_progExe.c_str());
|
||||||
|
::LogFinalise();
|
||||||
|
::ActivityLogFinalise();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
else if (pid != 0) {
|
||||||
|
::LogFinalise();
|
||||||
|
::ActivityLogFinalise();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new session and process group
|
||||||
|
if (::setsid() == -1) {
|
||||||
|
::fprintf(stderr, "%s: Couldn't setsid(), exiting\n", g_progExe.c_str());
|
||||||
|
::LogFinalise();
|
||||||
|
::ActivityLogFinalise();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the working directory to the root directory
|
||||||
|
if (::chdir("/") == -1) {
|
||||||
|
::fprintf(stderr, "%s: Couldn't cd /, exiting\n", g_progExe.c_str());
|
||||||
|
::LogFinalise();
|
||||||
|
::ActivityLogFinalise();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
::close(STDIN_FILENO);
|
||||||
|
::close(STDOUT_FILENO);
|
||||||
|
::close(STDERR_FILENO);
|
||||||
|
}
|
||||||
|
|
||||||
|
getHostVersion();
|
||||||
|
::LogInfo(">> Fixed Network Equipment");
|
||||||
|
|
||||||
|
// read base parameters from configuration
|
||||||
|
ret = readParams();
|
||||||
|
if (!ret)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
yaml::Node systemConf = m_conf["system"];
|
||||||
|
|
||||||
|
// try to load radio IDs table
|
||||||
|
std::string ridLookupFile = systemConf["radio_id"]["file"].as<std::string>();
|
||||||
|
uint32_t ridReloadTime = systemConf["radio_id"]["time"].as<uint32_t>(0U);
|
||||||
|
|
||||||
|
LogInfo("Radio Id Lookups");
|
||||||
|
LogInfo(" File: %s", ridLookupFile.length() > 0U ? ridLookupFile.c_str() : "None");
|
||||||
|
if (ridReloadTime > 0U)
|
||||||
|
LogInfo(" Reload: %u mins", ridReloadTime);
|
||||||
|
|
||||||
|
m_ridLookup = new RadioIdLookup(ridLookupFile, ridReloadTime, true);
|
||||||
|
m_ridLookup->read();
|
||||||
|
|
||||||
|
// initialize master networking
|
||||||
|
ret = createMasterNetwork();
|
||||||
|
if (!ret)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
// initialize peer networking
|
||||||
|
ret = createPeerNetworks();
|
||||||
|
if (!ret)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
::LogInfoEx(LOG_HOST, "FNE is up and running");
|
||||||
|
|
||||||
|
StopWatch stopWatch;
|
||||||
|
stopWatch.start();
|
||||||
|
|
||||||
|
// main execution loop
|
||||||
|
while (!g_killed) {
|
||||||
|
uint32_t ms = stopWatch.elapsed();
|
||||||
|
|
||||||
|
ms = stopWatch.elapsed();
|
||||||
|
stopWatch.start();
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// -- Network Clocking --
|
||||||
|
// ------------------------------------------------------
|
||||||
|
|
||||||
|
// clock master
|
||||||
|
if (m_network != nullptr)
|
||||||
|
m_network->clock(ms);
|
||||||
|
|
||||||
|
// clock peers
|
||||||
|
for (auto network : m_peerNetworks) {
|
||||||
|
network::Network* peerNetwork = network.second;
|
||||||
|
if (peerNetwork != nullptr) {
|
||||||
|
peerNetwork->clock(ms);
|
||||||
|
|
||||||
|
// process peer network traffic
|
||||||
|
processPeer(peerNetwork);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ms < 2U)
|
||||||
|
Thread::sleep(1U);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_network != nullptr) {
|
||||||
|
m_network->close();
|
||||||
|
delete m_network;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto network : m_peerNetworks) {
|
||||||
|
network::Network* peerNetwork = network.second;
|
||||||
|
if (peerNetwork != nullptr)
|
||||||
|
peerNetwork->close();
|
||||||
|
}
|
||||||
|
m_peerNetworks.clear();
|
||||||
|
|
||||||
|
if (m_tidLookup != nullptr) {
|
||||||
|
m_tidLookup->stop();
|
||||||
|
delete m_tidLookup;
|
||||||
|
}
|
||||||
|
if (m_ridLookup != nullptr) {
|
||||||
|
m_ridLookup->stop();
|
||||||
|
delete m_ridLookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Private Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads basic configuration parameters from the YAML configuration file.
|
||||||
|
/// </summary>
|
||||||
|
bool HostFNE::readParams()
|
||||||
|
{
|
||||||
|
yaml::Node systemConf = m_conf["system"];
|
||||||
|
m_pingTime = systemConf["pingTime"].as<uint32_t>(5U);
|
||||||
|
m_maxMissedPings = systemConf["maxMissedPings"].as<uint32_t>(5U);
|
||||||
|
m_updateLookupTime = systemConf["tgRuleUpdateTime"].as<uint32_t>(10U);
|
||||||
|
bool sendTalkgroups = systemConf["sendTalkgroups"].as<bool>(true);
|
||||||
|
|
||||||
|
if (m_pingTime == 0U) {
|
||||||
|
m_pingTime = 5U;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_maxMissedPings == 0U) {
|
||||||
|
m_maxMissedPings = 5U;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_updateLookupTime == 0U) {
|
||||||
|
m_updateLookupTime = 10U;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_allowActivityTransfer = systemConf["allowActivityTransfer"].as<bool>(true);
|
||||||
|
m_allowDiagnosticTransfer = systemConf["allowDiagnosticTransfer"].as<bool>(true);
|
||||||
|
|
||||||
|
LogInfo("General Parameters");
|
||||||
|
LogInfo(" Peer Ping Time: %us", m_pingTime);
|
||||||
|
LogInfo(" Maximum Missed Pings: %u", m_maxMissedPings);
|
||||||
|
LogInfo(" Talkgroup Rule Update Time: %u mins", m_updateLookupTime);
|
||||||
|
|
||||||
|
LogInfo(" Send Talkgroups: %s", sendTalkgroups ? "yes" : "no");
|
||||||
|
|
||||||
|
LogInfo(" Allow Activity Log Transfer: %s", m_allowActivityTransfer ? "yes" : "no");
|
||||||
|
LogInfo(" Allow Diagnostic Log Transfer: %s", m_allowDiagnosticTransfer ? "yes" : "no");
|
||||||
|
|
||||||
|
// attempt to load and populate routing rules
|
||||||
|
yaml::Node masterConf = m_conf["master"];
|
||||||
|
yaml::Node talkgroupRules = masterConf["talkgroup_rules"];
|
||||||
|
std::string talkgroupConfig = talkgroupRules["file"].as<std::string>();
|
||||||
|
uint32_t talkgroupConfigReload = talkgroupRules["time"].as<uint32_t>(30U);
|
||||||
|
|
||||||
|
LogInfo("Talkgroup Rule Lookups");
|
||||||
|
LogInfo(" File: %s", talkgroupConfig.length() > 0U ? talkgroupConfig.c_str() : "None");
|
||||||
|
if (talkgroupConfigReload > 0U)
|
||||||
|
LogInfo(" Reload: %u mins", talkgroupConfigReload);
|
||||||
|
|
||||||
|
m_tidLookup = new TalkgroupRulesLookup(talkgroupConfig, talkgroupConfigReload, true);
|
||||||
|
m_tidLookup->sendTalkgroups(sendTalkgroups);
|
||||||
|
m_tidLookup->read();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes master FNE network connectivity.
|
||||||
|
/// </summary>
|
||||||
|
bool HostFNE::createMasterNetwork()
|
||||||
|
{
|
||||||
|
yaml::Node masterConf = m_conf["master"];
|
||||||
|
std::string address = masterConf["address"].as<std::string>();
|
||||||
|
uint16_t port = (uint16_t)masterConf["port"].as<uint32_t>(TRAFFIC_DEFAULT_PORT);
|
||||||
|
uint32_t id = masterConf["peerId"].as<uint32_t>(1001U);
|
||||||
|
std::string password = masterConf["password"].as<std::string>();
|
||||||
|
bool verbose = masterConf["verbose"].as<bool>(false);
|
||||||
|
bool debug = masterConf["debug"].as<bool>(false);
|
||||||
|
|
||||||
|
m_dmrEnabled = masterConf["allowDMRTraffic"].as<bool>(true);
|
||||||
|
m_p25Enabled = masterConf["allowP25Traffic"].as<bool>(true);
|
||||||
|
m_nxdnEnabled = masterConf["allowNXDNTraffic"].as<bool>(true);
|
||||||
|
|
||||||
|
uint32_t parrotDelay = masterConf["parrotDelay"].as<uint32_t>(2500U);
|
||||||
|
if (m_pingTime * 1000U < parrotDelay) {
|
||||||
|
LogWarning(LOG_HOST, "Parrot delay cannot be longer then the ping time of a peer. Reducing parrot delay to half the ping time.");
|
||||||
|
parrotDelay = (m_pingTime * 1000U) / 2U;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogInfo("Network Parameters");
|
||||||
|
LogInfo(" Peer ID: %u", id);
|
||||||
|
LogInfo(" Address: %s", address.c_str());
|
||||||
|
LogInfo(" Port: %u", port);
|
||||||
|
LogInfo(" Allow DMR Traffic: %s", m_dmrEnabled ? "yes" : "no");
|
||||||
|
LogInfo(" Allow P25 Traffic: %s", m_p25Enabled ? "yes" : "no");
|
||||||
|
LogInfo(" Allow NXDN Traffic: %s", m_nxdnEnabled ? "yes" : "no");
|
||||||
|
LogInfo(" Parrot Repeat Delay: %u ms", parrotDelay);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
LogInfo(" Verbose: yes");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
LogInfo(" Debug: yes");
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize networking
|
||||||
|
m_network = new FNENetwork(this, address, port, id, password, debug, verbose, m_dmrEnabled, m_p25Enabled, m_nxdnEnabled, parrotDelay,
|
||||||
|
m_allowActivityTransfer, m_allowDiagnosticTransfer, m_pingTime, m_updateLookupTime);
|
||||||
|
|
||||||
|
m_network->setLookups(m_ridLookup, m_tidLookup);
|
||||||
|
|
||||||
|
bool ret = m_network->open();
|
||||||
|
if (!ret) {
|
||||||
|
delete m_network;
|
||||||
|
m_network = nullptr;
|
||||||
|
LogError(LOG_HOST, "failed to initialize traffic networking!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes peer FNE network connectivity.
|
||||||
|
/// </summary>
|
||||||
|
bool HostFNE::createPeerNetworks()
|
||||||
|
{
|
||||||
|
yaml::Node& peerList = m_conf["peers"];
|
||||||
|
if (peerList.size() > 0U) {
|
||||||
|
for (size_t i = 0; i < peerList.size(); i++) {
|
||||||
|
yaml::Node& peerConf = peerList[i];
|
||||||
|
|
||||||
|
bool enabled = peerConf["enabled"].as<bool>(false);
|
||||||
|
std::string address = peerConf["address"].as<std::string>();
|
||||||
|
uint16_t port = (uint16_t)peerConf["port"].as<uint32_t>(TRAFFIC_DEFAULT_PORT);
|
||||||
|
std::string masterAddress = peerConf["masterAddress"].as<std::string>();
|
||||||
|
uint16_t masterPort = (uint16_t)peerConf["masterPort"].as<uint32_t>(TRAFFIC_DEFAULT_PORT);
|
||||||
|
std::string password = peerConf["password"].as<std::string>();
|
||||||
|
uint32_t id = peerConf["peerId"].as<uint32_t>(1001U);
|
||||||
|
bool debug = peerConf["debug"].as<bool>(false);
|
||||||
|
|
||||||
|
std::string identity = peerConf["identity"].as<std::string>();
|
||||||
|
uint32_t rxFrequency = peerConf["rxFrequency"].as<uint32_t>(0U);
|
||||||
|
uint32_t txFrequency = peerConf["txFrequency"].as<uint32_t>(0U);
|
||||||
|
float latitude = peerConf["latitude"].as<float>(0.0F);
|
||||||
|
float longitude = peerConf["longitude"].as<float>(0.0F);
|
||||||
|
std::string location = peerConf["location"].as<std::string>();
|
||||||
|
|
||||||
|
::LogInfoEx(LOG_HOST, "Peer ID %u Master Address %s Master Port %u Identity %s Enabled %u", id, masterAddress.c_str(), masterPort, identity.c_str(), enabled);
|
||||||
|
|
||||||
|
// initialize networking
|
||||||
|
network::Network* network = new Network(address, port, 0U, id, password, true, debug, m_dmrEnabled, m_p25Enabled, m_nxdnEnabled, true, true, m_allowActivityTransfer, m_allowDiagnosticTransfer, false);
|
||||||
|
network->setMetadata(identity, rxFrequency, txFrequency, 0.0F, 0.0F, 0, 0, 0, latitude, longitude, 0, location);
|
||||||
|
|
||||||
|
network->enable(enabled);
|
||||||
|
if (enabled) {
|
||||||
|
bool ret = network->open();
|
||||||
|
if (!ret) {
|
||||||
|
LogError(LOG_HOST, "failed to initialize traffic networking for PEER %u", id);
|
||||||
|
network->enable(false);
|
||||||
|
network->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_peerNetworks[identity] = network;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes any peer network traffic.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="peerNetwork"></param>
|
||||||
|
void HostFNE::processPeer(network::Network* peerNetwork)
|
||||||
|
{
|
||||||
|
if (peerNetwork == nullptr)
|
||||||
|
return; // this shouldn't happen...
|
||||||
|
if (peerNetwork->getStatus() != NET_STAT_RUNNING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// process DMR data
|
||||||
|
if (peerNetwork->hasDMRData()) {
|
||||||
|
uint32_t length = 100U;
|
||||||
|
bool ret = false;
|
||||||
|
UInt8Array data = peerNetwork->readDMR(ret, length);
|
||||||
|
if (ret) {
|
||||||
|
uint32_t peerId = peerNetwork->getPeerId();
|
||||||
|
uint32_t slotNo = (data[15U] & 0x80U) == 0x80U ? 2U : 1U;
|
||||||
|
uint32_t streamId = peerNetwork->getDMRStreamId(slotNo);
|
||||||
|
|
||||||
|
m_network->dmrTrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process P25 data
|
||||||
|
if (peerNetwork->hasP25Data()) {
|
||||||
|
uint32_t length = 100U;
|
||||||
|
bool ret = false;
|
||||||
|
UInt8Array data = peerNetwork->readP25(ret, length);
|
||||||
|
if (ret) {
|
||||||
|
uint32_t peerId = peerNetwork->getPeerId();
|
||||||
|
uint32_t streamId = peerNetwork->getP25StreamId();
|
||||||
|
|
||||||
|
m_network->p25TrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process NXDN data
|
||||||
|
if (peerNetwork->hasNXDNData()) {
|
||||||
|
uint32_t length = 100U;
|
||||||
|
bool ret = false;
|
||||||
|
UInt8Array data = peerNetwork->readNXDN(ret, length);
|
||||||
|
if (ret) {
|
||||||
|
uint32_t peerId = peerNetwork->getPeerId();
|
||||||
|
uint32_t streamId = peerNetwork->getNXDNStreamId();
|
||||||
|
|
||||||
|
m_network->nxdnTrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__HOST_FNE_H__)
|
||||||
|
#define __HOST_FNE_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "network/Network.h"
|
||||||
|
#include "network/FNENetwork.h"
|
||||||
|
#include "Timer.h"
|
||||||
|
#include "lookups/RadioIdLookup.h"
|
||||||
|
#include "lookups/TalkgroupRulesLookup.h"
|
||||||
|
#include "yaml/Yaml.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the core FNE service logic.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API HostFNE {
|
||||||
|
public:
|
||||||
|
/// <summary>Initializes a new instance of the HostFNE class.</summary>
|
||||||
|
HostFNE(const std::string& confFile);
|
||||||
|
/// <summary>Finalizes a instance of the HostFNE class.</summary>
|
||||||
|
~HostFNE();
|
||||||
|
|
||||||
|
/// <summary>Executes the main FNE host processing loop.</summary>
|
||||||
|
int run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string& m_confFile;
|
||||||
|
yaml::Node m_conf;
|
||||||
|
|
||||||
|
friend class network::FNENetwork;
|
||||||
|
network::FNENetwork* m_network;
|
||||||
|
|
||||||
|
bool m_dmrEnabled;
|
||||||
|
bool m_p25Enabled;
|
||||||
|
bool m_nxdnEnabled;
|
||||||
|
|
||||||
|
lookups::RadioIdLookup* m_ridLookup;
|
||||||
|
lookups::TalkgroupRulesLookup* m_tidLookup;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, network::Network*> m_peerNetworks;
|
||||||
|
|
||||||
|
uint32_t m_pingTime;
|
||||||
|
uint32_t m_maxMissedPings;
|
||||||
|
uint32_t m_updateLookupTime;
|
||||||
|
|
||||||
|
bool m_allowActivityTransfer;
|
||||||
|
bool m_allowDiagnosticTransfer;
|
||||||
|
|
||||||
|
/// <summary>Reads basic configuration parameters from the INI.</summary>
|
||||||
|
bool readParams();
|
||||||
|
/// <summary>Initializes master FNE network connectivity.</summary>
|
||||||
|
bool createMasterNetwork();
|
||||||
|
/// <summary>Initializes peer FNE network connectivity.</summary>
|
||||||
|
bool createPeerNetworks();
|
||||||
|
|
||||||
|
/// <summary>Processes any peer network traffic.</summary>
|
||||||
|
void processPeer(network::Network* peerNetwork);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __HOST_FNE_H__
|
||||||
@ -0,0 +1,198 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__ADJUST_WND_BASE_H__)
|
||||||
|
#define __ADJUST_WND_BASE_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the base class for adjustment windows.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API AdjustWndBase : public finalcut::FDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdjustWndBase class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit AdjustWndBase(HostSetup* setup, FWidget* widget = nullptr) : FDialog{widget},
|
||||||
|
m_setup(setup)
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HostSetup *m_setup;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
virtual void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setMinimizable(true);
|
||||||
|
FDialog::setShadow();
|
||||||
|
|
||||||
|
std::size_t maxWidth, maxHeight;
|
||||||
|
const auto& rootWidget = getRootWidget();
|
||||||
|
|
||||||
|
if (rootWidget) {
|
||||||
|
maxWidth = rootWidget->getClientWidth();
|
||||||
|
maxHeight = rootWidget->getClientHeight();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// fallback to xterm default size
|
||||||
|
maxWidth = 80;
|
||||||
|
maxHeight = 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int x = 1 + int((maxWidth - getWidth()) / 2);
|
||||||
|
const int y = 1 + int((maxHeight - getHeight()) / 3);
|
||||||
|
FWindow::setPos(FPoint{x, y}, false);
|
||||||
|
FDialog::adjustSize();
|
||||||
|
|
||||||
|
FDialog::setModal();
|
||||||
|
|
||||||
|
initControls();
|
||||||
|
|
||||||
|
FDialog::initLayout();
|
||||||
|
|
||||||
|
rootWidget->redraw(); // bryanb: wtf?
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
virtual void initControls()
|
||||||
|
{
|
||||||
|
// transmit button and close button logic
|
||||||
|
m_txButton.setGeometry(FPoint(3, int(getHeight()) - 6), FSize(10, 3));
|
||||||
|
if (!m_setup->m_isConnected) {
|
||||||
|
m_txButton.setDisable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set transmit button color state if connected
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
if (m_setup->m_transmit) {
|
||||||
|
m_txButton.setBackgroundColor(FColor::Red3);
|
||||||
|
m_txButton.setFocusBackgroundColor(FColor::Red3);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_txButton.resetColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_txButton.redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_txButton.addCallback("clicked", [&]() { setTransmit(); });
|
||||||
|
|
||||||
|
|
||||||
|
m_closeButton.setGeometry(FPoint(17, int(getHeight()) - 6), FSize(9, 3));
|
||||||
|
m_closeButton.addCallback("clicked", [&]() { hide(); });
|
||||||
|
|
||||||
|
m_connectedLabel.setGeometry(FPoint(36, int(getHeight()) - 3), FSize(20, 3));
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
m_connectedLabel.setText("Modem Connected");
|
||||||
|
m_connectedLabel.setForegroundColor(FColor::Green3);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_connectedLabel.setText("Modem Disconnected");
|
||||||
|
m_connectedLabel.setForegroundColor(FColor::Red3);
|
||||||
|
}
|
||||||
|
|
||||||
|
focusFirstChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
virtual void adjustSize() override
|
||||||
|
{
|
||||||
|
FDialog::adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Event Handlers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
virtual void onKeyPress(finalcut::FKeyEvent* e)
|
||||||
|
{
|
||||||
|
const auto key = e->key();
|
||||||
|
if (key == FKey::F12) {
|
||||||
|
setTransmit();
|
||||||
|
}
|
||||||
|
else if (key == FKey::F2) {
|
||||||
|
m_setup->saveConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
virtual void onClose(FCloseEvent* e) override
|
||||||
|
{
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FLabel m_connectedLabel{"Modem Disconnected", this};
|
||||||
|
|
||||||
|
FButton m_txButton{"Transmit", this};
|
||||||
|
FButton m_closeButton{"Close", this};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void setTransmit()
|
||||||
|
{
|
||||||
|
if (!m_setup->setTransmit()) {
|
||||||
|
FMessageBox::error(this, "Failed to enable modem transmit!");
|
||||||
|
}
|
||||||
|
if (m_setup->m_transmit) {
|
||||||
|
m_txButton.setBackgroundColor(FColor::Red3);
|
||||||
|
m_txButton.setFocusBackgroundColor(FColor::Red3);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_txButton.resetColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_txButton.redraw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __ADJUST_WND_BASE_H__
|
||||||
@ -0,0 +1,232 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// Based on code from the finalcut project. (https://github.com/gansm/finalcut)
|
||||||
|
// Licensed under the LGPLv2 License (https://opensource.org/licenses/LGPL-2.0)
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2023 by Markus Gans
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__BER_DISPLAY_WND_H__)
|
||||||
|
#define __BER_DISPLAY_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the bit error rate display window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API BERDisplayWnd final : public finalcut::FDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the BERDisplayWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit BERDisplayWnd(FWidget* widget = nullptr) : FDialog{widget}
|
||||||
|
{
|
||||||
|
m_code = {
|
||||||
|
/*
|
||||||
|
** Segments are drawn as follows:
|
||||||
|
**
|
||||||
|
** H A I
|
||||||
|
** F G B
|
||||||
|
** E D C
|
||||||
|
*/
|
||||||
|
// h v v h v v h h v
|
||||||
|
// a b c d e f g h i
|
||||||
|
{ '0', Segment{1, 1, 1, 1, 1, 1, 0, 1, 2} },
|
||||||
|
{ '1', Segment{0, 1, 1, 0, 0, 0, 0, 0, 2} },
|
||||||
|
{ '2', Segment{1, 1, 2, 1, 1, 2, 1, 1, 2} },
|
||||||
|
{ '3', Segment{1, 1, 1, 1, 2, 0, 1, 1, 2} },
|
||||||
|
{ '4', Segment{0, 1, 1, 0, 0, 1, 1, 1, 2} },
|
||||||
|
{ '5', Segment{1, 2, 1, 1, 2, 1, 1, 1, 2} },
|
||||||
|
{ '6', Segment{1, 2, 1, 1, 1, 1, 1, 1, 2} },
|
||||||
|
{ '7', Segment{1, 1, 1, 0, 0, 0, 0, 1, 2} },
|
||||||
|
{ '8', Segment{1, 1, 1, 1, 1, 1, 1, 1, 2} },
|
||||||
|
{ '9', Segment{1, 1, 1, 1, 2, 1, 1, 1, 2} },
|
||||||
|
{ 'A', Segment{1, 1, 1, 0, 1, 1, 1, 1, 2} },
|
||||||
|
{ 'B', Segment{0, 2, 1, 1, 1, 1, 1, 1, 0} },
|
||||||
|
{ 'C', Segment{1, 0, 2, 1, 1, 1, 0, 1, 2} },
|
||||||
|
{ 'D', Segment{0, 1, 1, 1, 1, 2, 1, 0, 2} },
|
||||||
|
{ 'E', Segment{1, 0, 2, 1, 1, 1, 1, 1, 2} },
|
||||||
|
{ 'F', Segment{1, 0, 0, 0, 1, 1, 1, 1, 2} }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/// <summary>Copy constructor.</summary>
|
||||||
|
BERDisplayWnd(const BERDisplayWnd&) = delete;
|
||||||
|
/// <summary>Move constructor.</summary>
|
||||||
|
BERDisplayWnd(BERDisplayWnd&&) noexcept = delete;
|
||||||
|
/// <summary>Finalizes an instance of the ModemStatusWnd class.</summary>
|
||||||
|
~BERDisplayWnd() noexcept override = default;
|
||||||
|
|
||||||
|
/// <summary>Disable copy assignment operator (=).</summary>
|
||||||
|
auto operator= (const BERDisplayWnd&) -> BERDisplayWnd& = delete;
|
||||||
|
/// <summary>Disable move assignment operator (=).</summary>
|
||||||
|
auto operator= (BERDisplayWnd&&) noexcept -> BERDisplayWnd& = delete;
|
||||||
|
|
||||||
|
/// <summary>Disable set X coordinate.</summary>
|
||||||
|
void setX(int, bool = true) override { }
|
||||||
|
/// <summary>Disable set Y coordinate.</summary>
|
||||||
|
void setY(int, bool = true) override { }
|
||||||
|
/// <summary>Disable set position.</summary>
|
||||||
|
void setPos(const FPoint&, bool = true) override { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to set the BER text.
|
||||||
|
/// </summary>
|
||||||
|
void ber(std::string str)
|
||||||
|
{
|
||||||
|
if (str.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ber = str;
|
||||||
|
std::transform(m_ber.begin(), m_ber.end(), m_ber.begin(), ::toupper);
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Helper to set the segment color.</summary>
|
||||||
|
void segmentColor(FColor color) { m_segmentColor = color; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_ber;
|
||||||
|
|
||||||
|
struct Segment
|
||||||
|
{
|
||||||
|
unsigned char a : 2;
|
||||||
|
unsigned char b : 2;
|
||||||
|
unsigned char c : 2;
|
||||||
|
unsigned char d : 2;
|
||||||
|
unsigned char e : 2;
|
||||||
|
unsigned char f : 2;
|
||||||
|
unsigned char g : 2;
|
||||||
|
unsigned char h : 2;
|
||||||
|
unsigned char i : 2;
|
||||||
|
unsigned char : 2; // padding bit
|
||||||
|
};
|
||||||
|
std::map<wchar_t, Segment> m_code{};
|
||||||
|
std::array<FString, 3> m_line{};
|
||||||
|
|
||||||
|
FColor m_segmentColor{FColor::LightRed};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setText("Receive BER");
|
||||||
|
|
||||||
|
const auto& rootWidget = getRootWidget();
|
||||||
|
|
||||||
|
FDialog::setGeometry(FPoint{(int)rootWidget->getClientWidth() - 26, 2}, FSize{25, 7});
|
||||||
|
FDialog::setMinimumSize(FSize{25, 7});
|
||||||
|
FDialog::setResizeable(false);
|
||||||
|
FDialog::setMinimizable(false);
|
||||||
|
FDialog::setTitlebarButtonVisibility(false);
|
||||||
|
FDialog::setShadow(false);
|
||||||
|
FDialog::setAlwaysOnTop(true);
|
||||||
|
|
||||||
|
FDialog::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void draw() override
|
||||||
|
{
|
||||||
|
std::vector<FVTermBuffer> vtbuffer(3);
|
||||||
|
FDialog::draw();
|
||||||
|
|
||||||
|
setColor(FColor::LightGray, FColor::Black);
|
||||||
|
finalcut::drawBorder(this, FRect(FPoint{1, 2}, FPoint{25, 7}));
|
||||||
|
|
||||||
|
for (const auto& ch : m_ber) {
|
||||||
|
const FColorPair color{m_segmentColor, FColor::Black};
|
||||||
|
get7Segment(ch);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < 3; i++)
|
||||||
|
vtbuffer[i] << color << m_line[i] << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t length = vtbuffer[0].getLength();
|
||||||
|
const FVTermBuffer leftSpace = length < 23 ? FVTermBuffer() << FString(23 - length, ' ') : FVTermBuffer();
|
||||||
|
print() << FPoint{2, 3} << leftSpace << vtbuffer[0]
|
||||||
|
<< FPoint{2, 4} << leftSpace << vtbuffer[1]
|
||||||
|
<< FPoint{2, 5} << leftSpace << vtbuffer[2]
|
||||||
|
<< FPoint{2, 6} << FString{23, ' '};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c"></param>
|
||||||
|
void get7Segment(const wchar_t c)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < 3; i++)
|
||||||
|
m_line[i].clear();
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case ':':
|
||||||
|
m_line[0] = ' ';
|
||||||
|
m_line[1] = '.';
|
||||||
|
m_line[2] = '.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '.':
|
||||||
|
m_line[0] = ' ';
|
||||||
|
m_line[1] = ' ';
|
||||||
|
m_line[2] = (wchar_t)(0x2584);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
m_line[0] << ' ' << ' ' << ' ';
|
||||||
|
m_line[1] << (wchar_t)(0x2584) << (wchar_t)(0x2584) << (wchar_t)(0x2584);
|
||||||
|
m_line[2] << ' ' << ' ' << ' ';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// hexadecimal digit from 0 up to F
|
||||||
|
if (m_code.find(c) != m_code.end()) {
|
||||||
|
const Segment& s = m_code[c];
|
||||||
|
constexpr std::array<wchar_t, 3> h{{0x20, 0x2584, 0x2588}};
|
||||||
|
constexpr std::array<wchar_t, 3> v{{0x20, 0x2588, 0x2584}};
|
||||||
|
|
||||||
|
m_line[0] << h[s.h] << h[s.a] << v[s.i];
|
||||||
|
m_line[1] << v[s.f] << h[s.g] << v[s.b];
|
||||||
|
m_line[2] << v[s.e] << h[s.d] << v[s.c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __BER_DISPLAY_WND_H__
|
||||||
@ -0,0 +1,240 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__CHANNEL_CONFIG_SET_WND_H__)
|
||||||
|
#define __CHANNEL_CONFIG_SET_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include "host/setup/CloseWndBase.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the channel configuration window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API ChannelConfigSetWnd final : public CloseWndBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the ChannelConfigSetWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit ChannelConfigSetWnd(HostSetup* setup, FWidget* widget = nullptr) : CloseWndBase{setup, widget}
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FLabel m_channelIdLabel{"Channel ID: ", this};
|
||||||
|
FSpinBox m_channelId{this};
|
||||||
|
|
||||||
|
FLabel m_baseFreqLabel{"Base Freq. (Hz): ", this};
|
||||||
|
FLabel m_baseFreq{this};
|
||||||
|
FLabel m_spaceHzLabel{"Spacing (Hz): ", this};
|
||||||
|
FLabel m_spaceHz{this};
|
||||||
|
|
||||||
|
FButtonGroup m_chNoGroup{"Logical Channel Number", this};
|
||||||
|
FRadioButton m_radioChNo{"Channel Number", &m_chNoGroup};
|
||||||
|
FRadioButton m_radioChFreq{"Tx Frequency", &m_chNoGroup};
|
||||||
|
|
||||||
|
FLabel m_channelNoLabel{"Channel No.: ", this};
|
||||||
|
FSpinBox m_channelNo{this};
|
||||||
|
bool m_displayChannelFreq = false;
|
||||||
|
FLabel m_channelFreqLabel{"Tx Frequency: ", this};
|
||||||
|
FSpinBox m_channelFreq{this};
|
||||||
|
|
||||||
|
FLabel m_hzLabel{"Hz", this};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setText("Channel Configuration");
|
||||||
|
FDialog::setSize(FSize{60, 17});
|
||||||
|
|
||||||
|
m_enableSetButton = false;
|
||||||
|
CloseWndBase::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initControls()
|
||||||
|
{
|
||||||
|
yaml::Node rfssConfig = m_setup->m_conf["system"]["config"];
|
||||||
|
m_setup->m_channelId = (uint8_t)rfssConfig["channelId"].as<uint32_t>(0U);
|
||||||
|
IdenTable entry = m_setup->m_idenTable->find(m_setup->m_channelId);
|
||||||
|
|
||||||
|
// channel ID and channel number type
|
||||||
|
{
|
||||||
|
m_channelIdLabel.setGeometry(FPoint(2, 2), FSize(20, 1));
|
||||||
|
m_channelId.setGeometry(FPoint(23, 2), FSize(8, 1));
|
||||||
|
m_channelId.setValue(m_setup->m_channelId);
|
||||||
|
m_channelId.setRange(0, 15);
|
||||||
|
m_channelId.setShadow(false);
|
||||||
|
m_channelId.addCallback("changed", [&]() {
|
||||||
|
uint8_t prevChannelId = m_setup->m_channelId;
|
||||||
|
m_setup->m_channelId = (uint8_t)(m_channelId.getValue());
|
||||||
|
|
||||||
|
entry = m_setup->m_idenTable->find(m_setup->m_channelId);
|
||||||
|
if (entry.baseFrequency() == 0U) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Channel Id " << (uint32_t)(m_setup->m_channelId) << " has an invalid base frequency.";
|
||||||
|
FMessageBox::error(this, ss.str());
|
||||||
|
m_setup->m_channelId = prevChannelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = m_setup->m_idenTable->find(m_setup->m_channelId);
|
||||||
|
m_baseFreq.setText(__INT_STR(entry.baseFrequency()));
|
||||||
|
m_spaceHz.setText(__INT_STR(entry.chSpaceKhz() * 1000));
|
||||||
|
|
||||||
|
m_setup->m_conf["system"]["config"]["channelId"] = __INT_STR(m_setup->m_channelId);
|
||||||
|
m_setup->calculateRxTxFreq();
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_baseFreqLabel.setGeometry(FPoint(2, 4), FSize(20, 1));
|
||||||
|
m_baseFreq.setGeometry(FPoint(23, 4), FSize(20, 1));
|
||||||
|
m_baseFreq.setText(__INT_STR(entry.baseFrequency()));
|
||||||
|
m_spaceHzLabel.setGeometry(FPoint(2, 5), FSize(20, 1));
|
||||||
|
m_spaceHz.setGeometry(FPoint(23, 5), FSize(20, 1));
|
||||||
|
m_spaceHz.setText(__INT_STR(entry.chSpaceKhz() * 1000));
|
||||||
|
|
||||||
|
m_chNoGroup.setGeometry(FPoint(2, 7), FSize(56, 2));
|
||||||
|
m_radioChNo.setPos(FPoint(1, 1));
|
||||||
|
m_radioChNo.addCallback("toggled", [&]() {
|
||||||
|
if (m_radioChNo.isChecked()) {
|
||||||
|
m_displayChannelFreq = false;
|
||||||
|
updateVisibleControls();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_radioChFreq.setPos(FPoint(23, 1));
|
||||||
|
m_radioChFreq.addCallback("toggled", [&]() {
|
||||||
|
if (m_radioChFreq.isChecked()) {
|
||||||
|
m_displayChannelFreq = true;
|
||||||
|
updateVisibleControls();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// channel number
|
||||||
|
{
|
||||||
|
m_setup->m_channelNo = (uint32_t)::strtoul(rfssConfig["channelNo"].as<std::string>("1").c_str(), NULL, 16);
|
||||||
|
|
||||||
|
m_channelNoLabel.setGeometry(FPoint(2, 11), FSize(20, 1));
|
||||||
|
m_channelNo.setGeometry(FPoint(23, 11), FSize(15, 1));
|
||||||
|
m_channelNo.setValue(m_setup->m_channelNo);
|
||||||
|
m_channelNo.setRange(0, 4095);
|
||||||
|
m_channelNo.setShadow(false);
|
||||||
|
m_channelNo.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["config"]["channelNo"] = __INT_HEX_STR(m_channelNo.getValue());
|
||||||
|
m_setup->calculateRxTxFreq();
|
||||||
|
m_channelFreq.setValue(m_setup->m_txFrequency);
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// channel frequency
|
||||||
|
{
|
||||||
|
m_setup->m_channelNo = (uint32_t)::strtoul(rfssConfig["channelNo"].as<std::string>("1").c_str(), NULL, 16);
|
||||||
|
|
||||||
|
m_channelFreqLabel.setGeometry(FPoint(2, 12), FSize(20, 1));
|
||||||
|
m_channelFreq.setGeometry(FPoint(23, 12), FSize(15, 1));
|
||||||
|
m_channelFreq.setValue(m_setup->m_txFrequency);
|
||||||
|
m_channelFreq.setShadow(false);
|
||||||
|
m_channelFreq.addCallback("changed", [&]() {
|
||||||
|
entry = m_setup->m_idenTable->find(m_setup->m_channelId);
|
||||||
|
|
||||||
|
uint32_t txFrequency = m_channelFreq.getValue();
|
||||||
|
|
||||||
|
uint32_t prevTxFrequency = m_setup->m_txFrequency;
|
||||||
|
m_setup->m_txFrequency = txFrequency;
|
||||||
|
uint32_t prevRxFrequency = m_setup->m_rxFrequency;
|
||||||
|
m_setup->m_rxFrequency = m_setup->m_txFrequency + (uint32_t)(entry.txOffsetMhz() * 1000000);
|
||||||
|
|
||||||
|
float spaceHz = entry.chSpaceKhz() * 1000;
|
||||||
|
|
||||||
|
uint32_t rootFreq = m_setup->m_txFrequency - entry.baseFrequency();
|
||||||
|
uint8_t prevChannelNo = m_setup->m_channelNo;
|
||||||
|
m_setup->m_channelNo = (uint32_t)(rootFreq / spaceHz);
|
||||||
|
|
||||||
|
if (m_setup->m_channelNo < 0 || m_setup->m_channelNo > 4096) {
|
||||||
|
m_setup->m_channelNo = prevChannelNo;
|
||||||
|
m_setup->m_txFrequency = prevTxFrequency;
|
||||||
|
m_setup->m_rxFrequency = prevRxFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_setup->m_conf["system"]["config"]["channelNo"] = __INT_HEX_STR(m_setup->m_channelNo);
|
||||||
|
m_setup->calculateRxTxFreq();
|
||||||
|
m_channelNo.setValue(m_setup->m_channelNo);
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_hzLabel.setGeometry(FPoint(40, 12), FSize(5, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVisibleControls();
|
||||||
|
|
||||||
|
CloseWndBase::initControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void updateVisibleControls()
|
||||||
|
{
|
||||||
|
if (m_displayChannelFreq) {
|
||||||
|
m_channelNoLabel.setDisable();
|
||||||
|
m_channelNo.setDisable();
|
||||||
|
|
||||||
|
m_channelFreqLabel.setEnable();
|
||||||
|
m_channelFreq.setEnable();
|
||||||
|
|
||||||
|
redraw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_channelNoLabel.setEnable();
|
||||||
|
m_channelNo.setEnable();
|
||||||
|
m_channelFreqLabel.setDisable();
|
||||||
|
m_channelFreq.setDisable();
|
||||||
|
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __CHANNEL_CONFIG_SET_WND_H__
|
||||||
@ -0,0 +1,152 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__CLOSE_WND_BASE_H__)
|
||||||
|
#define __CLOSE_WND_BASE_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the base class for windows with close buttons.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API CloseWndBase : public finalcut::FDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the CloseWndBase class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit CloseWndBase(HostSetup* setup, FWidget* widget = nullptr) : FDialog{widget},
|
||||||
|
m_setup(setup)
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HostSetup *m_setup;
|
||||||
|
|
||||||
|
bool m_enableSetButton;
|
||||||
|
FButton m_setButton{"Set", this};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
virtual void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setMinimizable(true);
|
||||||
|
FDialog::setShadow();
|
||||||
|
|
||||||
|
std::size_t maxWidth, maxHeight;
|
||||||
|
const auto& rootWidget = getRootWidget();
|
||||||
|
|
||||||
|
if (rootWidget) {
|
||||||
|
maxWidth = rootWidget->getClientWidth();
|
||||||
|
maxHeight = rootWidget->getClientHeight();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// fallback to xterm default size
|
||||||
|
maxWidth = 80;
|
||||||
|
maxHeight = 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int x = 1 + int((maxWidth - getWidth()) / 2);
|
||||||
|
const int y = 1 + int((maxHeight - getHeight()) / 3);
|
||||||
|
FWindow::setPos(FPoint{x, y}, false);
|
||||||
|
FDialog::adjustSize();
|
||||||
|
|
||||||
|
FDialog::setModal();
|
||||||
|
|
||||||
|
initControls();
|
||||||
|
|
||||||
|
FDialog::initLayout();
|
||||||
|
|
||||||
|
rootWidget->redraw(); // bryanb: wtf?
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
virtual void initControls()
|
||||||
|
{
|
||||||
|
m_closeButton.setGeometry(FPoint(int(getWidth()) - 12, int(getHeight()) - 6), FSize(9, 3));
|
||||||
|
m_closeButton.addCallback("clicked", [&]() { hide(); });
|
||||||
|
|
||||||
|
m_setButton.setDisable();
|
||||||
|
m_setButton.setVisible(false);
|
||||||
|
if (m_enableSetButton) {
|
||||||
|
m_setButton.setEnable();
|
||||||
|
m_setButton.setVisible(true);
|
||||||
|
m_setButton.setGeometry(FPoint(int(getWidth()) - 24, int(getHeight()) - 6), FSize(9, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
focusFirstChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
virtual void adjustSize() override
|
||||||
|
{
|
||||||
|
FDialog::adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Event Handlers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
virtual void onKeyPress(finalcut::FKeyEvent* e)
|
||||||
|
{
|
||||||
|
const auto key = e->key();
|
||||||
|
if (key == FKey::F2) {
|
||||||
|
m_setup->saveConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
virtual void onClose(FCloseEvent* e) override
|
||||||
|
{
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FButton m_closeButton{"Close", this};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __CLOSE_WND_BASE_H__
|
||||||
@ -0,0 +1,125 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__FIFO_BUFFER_ADJUST_WND_H__)
|
||||||
|
#define __FIFO_BUFFER_ADJUST_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include "host/setup/CloseWndBase.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the FIFO buffer adjustment window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API FIFOBufferAdjustWnd final : public CloseWndBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the FIFOBufferAdjustWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit FIFOBufferAdjustWnd(HostSetup* setup, FWidget* widget = nullptr) : CloseWndBase{setup, widget}
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FLabel m_fifoBufferLabel{"FIFO Buffers", this};
|
||||||
|
FLabel m_dmrBufferLabel{"DMR Buffer (bytes): ", this};
|
||||||
|
FLabel m_p25BufferLabel{"P25 Buffer (bytes): ", this};
|
||||||
|
FLabel m_nxdnBufferLabel{"NXDN Buffer (bytes): ", this};
|
||||||
|
|
||||||
|
FSpinBox m_dmrBuffer{this};
|
||||||
|
FSpinBox m_p25Buffer{this};
|
||||||
|
FSpinBox m_nxdnBuffer{this};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setText("FIFO Buffer Adjustment");
|
||||||
|
FDialog::setSize(FSize{60, 13});
|
||||||
|
|
||||||
|
m_enableSetButton = true;
|
||||||
|
CloseWndBase::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initControls()
|
||||||
|
{
|
||||||
|
// symbol levels
|
||||||
|
{
|
||||||
|
m_fifoBufferLabel.setGeometry(FPoint(2, 1), FSize(20, 2));
|
||||||
|
m_fifoBufferLabel.setEmphasis();
|
||||||
|
m_fifoBufferLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_dmrBufferLabel.setGeometry(FPoint(2, 3), FSize(25, 1));
|
||||||
|
m_dmrBuffer.setGeometry(FPoint(28, 3), FSize(10, 1));
|
||||||
|
m_dmrBuffer.setRange(DMR_TX_BUFFER_LEN, 65535);
|
||||||
|
m_dmrBuffer.setValue(m_setup->m_modem->m_dmrFifoLength);
|
||||||
|
m_dmrBuffer.setShadow(false);
|
||||||
|
m_dmrBuffer.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_dmrFifoLength = m_dmrBuffer.getValue();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_p25BufferLabel.setGeometry(FPoint(2, 4), FSize(25, 1));
|
||||||
|
m_p25Buffer.setGeometry(FPoint(28, 4), FSize(10, 1));
|
||||||
|
m_p25Buffer.setRange(P25_TX_BUFFER_LEN, 65535);
|
||||||
|
m_p25Buffer.setValue(m_setup->m_modem->m_p25FifoLength);
|
||||||
|
m_p25Buffer.setShadow(false);
|
||||||
|
m_p25Buffer.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_p25FifoLength = m_p25Buffer.getValue();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_nxdnBufferLabel.setGeometry(FPoint(2, 5), FSize(25, 1));
|
||||||
|
m_nxdnBuffer.setGeometry(FPoint(28, 5), FSize(10, 1));
|
||||||
|
m_nxdnBuffer.setRange(NXDN_TX_BUFFER_LEN, 65535);
|
||||||
|
m_nxdnBuffer.setValue(m_setup->m_modem->m_nxdnFifoLength);
|
||||||
|
m_nxdnBuffer.setShadow(false);
|
||||||
|
m_nxdnBuffer.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_nxdnFifoLength = m_nxdnBuffer.getValue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
m_setButton.addCallback("clicked", [&]() {
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeFifoLength();
|
||||||
|
});
|
||||||
|
|
||||||
|
CloseWndBase::initControls();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __FIFO_BUFFER_ADJUST_WND_H__
|
||||||
@ -0,0 +1,184 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__HS_BANDWIDTH_ADJUST_WND_H__)
|
||||||
|
#define __HS_BANDWIDTH_ADJUST_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include "host/setup/AdjustWndBase.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the hotspot bandwidth adjustment window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API HSBandwidthAdjustWnd final : public AdjustWndBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the HSBandwidthAdjustWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit HSBandwidthAdjustWnd(HostSetup* setup, FWidget* widget = nullptr) : AdjustWndBase{setup, widget}
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FLabel m_adjLevelLabel{"Bandwidth Adjustment", this};
|
||||||
|
FLabel m_dmrDiscBWLabel{"DMR Disc. BW Offset: ", this};
|
||||||
|
FLabel m_dmrPostBWLabel{"DMR Post Demod BW Offset: ", this};
|
||||||
|
FLabel m_p25DiscBWLabel{"P25 Disc. BW Offset: ", this};
|
||||||
|
FLabel m_p25PostBWLabel{"P25 Post Demod BW Offset: ", this};
|
||||||
|
FLabel m_nxdnDiscBWLabel{"NXDN Disc. BW Offset: ", this};
|
||||||
|
FLabel m_nxdnPostBWLabel{"NXDN Post Demod BW Offset: ", this};
|
||||||
|
|
||||||
|
FSpinBox m_dmrDiscBW{this};
|
||||||
|
FSpinBox m_dmrPostBW{this};
|
||||||
|
FSpinBox m_p25DiscBW{this};
|
||||||
|
FSpinBox m_p25PostBW{this};
|
||||||
|
FSpinBox m_nxdnDiscBW{this};
|
||||||
|
FSpinBox m_nxdnPostBW{this};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setText("Hotspot Bandwidth Adjustment");
|
||||||
|
FDialog::setSize(FSize{60, 15});
|
||||||
|
|
||||||
|
AdjustWndBase::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initControls()
|
||||||
|
{
|
||||||
|
// symbol levels
|
||||||
|
{
|
||||||
|
m_adjLevelLabel.setGeometry(FPoint(2, 1), FSize(30, 2));
|
||||||
|
m_adjLevelLabel.setEmphasis();
|
||||||
|
m_adjLevelLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_dmrDiscBWLabel.setGeometry(FPoint(2, 3), FSize(30, 1));
|
||||||
|
m_dmrDiscBW.setGeometry(FPoint(33, 3), FSize(10, 1));
|
||||||
|
m_dmrDiscBW.setRange(-127, 127);
|
||||||
|
m_dmrDiscBW.setValue(m_setup->m_modem->m_dmrDiscBWAdj);
|
||||||
|
m_dmrDiscBW.setShadow(false);
|
||||||
|
m_dmrDiscBW.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_dmrDiscBWAdj = m_dmrDiscBW.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_dmrPostBWLabel.setGeometry(FPoint(2, 4), FSize(30, 1));
|
||||||
|
m_dmrPostBW.setGeometry(FPoint(33, 4), FSize(10, 1));
|
||||||
|
m_dmrPostBW.setRange(-127, 127);
|
||||||
|
m_dmrPostBW.setValue(m_setup->m_modem->m_dmrPostBWAdj);
|
||||||
|
m_dmrPostBW.setShadow(false);
|
||||||
|
m_dmrPostBW.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_dmrPostBWAdj = m_dmrPostBW.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_p25DiscBWLabel.setGeometry(FPoint(2, 5), FSize(30, 1));
|
||||||
|
m_p25DiscBW.setGeometry(FPoint(33, 5), FSize(10, 1));
|
||||||
|
m_p25DiscBW.setRange(-127, 127);
|
||||||
|
m_p25DiscBW.setValue(m_setup->m_modem->m_p25DiscBWAdj);
|
||||||
|
m_p25DiscBW.setShadow(false);
|
||||||
|
m_p25DiscBW.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_p25DiscBWAdj = m_p25DiscBW.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_p25PostBWLabel.setGeometry(FPoint(2, 6), FSize(30, 1));
|
||||||
|
m_p25PostBW.setGeometry(FPoint(33, 6), FSize(10, 1));
|
||||||
|
m_p25PostBW.setRange(-127, 127);
|
||||||
|
m_p25PostBW.setValue(m_setup->m_modem->m_p25PostBWAdj);
|
||||||
|
m_p25PostBW.setShadow(false);
|
||||||
|
m_p25PostBW.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_p25PostBWAdj = m_p25PostBW.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_nxdnDiscBWLabel.setGeometry(FPoint(2, 6), FSize(30, 1));
|
||||||
|
m_nxdnDiscBW.setGeometry(FPoint(33, 6), FSize(10, 1));
|
||||||
|
m_nxdnDiscBW.setRange(-127, 127);
|
||||||
|
m_nxdnDiscBW.setValue(m_setup->m_modem->m_nxdnDiscBWAdj);
|
||||||
|
m_nxdnDiscBW.setShadow(false);
|
||||||
|
m_nxdnDiscBW.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_nxdnDiscBWAdj = m_nxdnDiscBW.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_nxdnPostBWLabel.setGeometry(FPoint(2, 7), FSize(30, 1));
|
||||||
|
m_nxdnPostBW.setGeometry(FPoint(33, 7), FSize(10, 1));
|
||||||
|
m_nxdnPostBW.setRange(-127, 127);
|
||||||
|
m_nxdnPostBW.setValue(m_setup->m_modem->m_nxdnPostBWAdj);
|
||||||
|
m_nxdnPostBW.setShadow(false);
|
||||||
|
m_nxdnPostBW.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_nxdnPostBWAdj = m_nxdnPostBW.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup control states
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
if (m_setup->m_modem->m_isHotspot) {
|
||||||
|
m_dmrDiscBW.setEnable();
|
||||||
|
m_dmrPostBW.setEnable();
|
||||||
|
m_p25DiscBW.setEnable();
|
||||||
|
m_p25PostBW.setEnable();
|
||||||
|
m_nxdnDiscBW.setEnable();
|
||||||
|
m_nxdnPostBW.setEnable();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_dmrDiscBW.setDisable();
|
||||||
|
m_dmrPostBW.setDisable();
|
||||||
|
m_p25DiscBW.setDisable();
|
||||||
|
m_p25PostBW.setDisable();
|
||||||
|
m_nxdnDiscBW.setDisable();
|
||||||
|
m_nxdnPostBW.setDisable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AdjustWndBase::initControls();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __HS_BANDWIDTH_ADJUST_WND_H__
|
||||||
@ -0,0 +1,179 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__HS_GAIN_ADJUST_WND_H__)
|
||||||
|
#define __HS_GAIN_ADJUST_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include "host/setup/AdjustWndBase.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the hotspot gain adjustment window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API HSGainAdjustWnd final : public AdjustWndBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the HSGainAdjustWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit HSGainAdjustWnd(HostSetup* setup, FWidget* widget = nullptr) : AdjustWndBase{setup, widget}
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FLabel m_adjGainLabel{"Gain Adjustment", this};
|
||||||
|
|
||||||
|
FButtonGroup m_gainButtonGroup{"Gain", this};
|
||||||
|
FRadioButton m_gainAHL{"Auto High Linearity", &m_gainButtonGroup};
|
||||||
|
FRadioButton m_gainLow{"Low", &m_gainButtonGroup};
|
||||||
|
FRadioButton m_gainHigh{"High", &m_gainButtonGroup};
|
||||||
|
FRadioButton m_gainAuto{"Auto", &m_gainButtonGroup};
|
||||||
|
|
||||||
|
FLabel m_adjAFCLabel{"AFC Adjustment", this};
|
||||||
|
|
||||||
|
FCheckBox m_afcEnabled{"Enabled", this};
|
||||||
|
FLabel m_afcRangeLabel{"Range: ", this};
|
||||||
|
FSpinBox m_afcRange{this};
|
||||||
|
FLabel m_afcKILabel{"KI: ", this};
|
||||||
|
FSpinBox m_afcKI{this};
|
||||||
|
FLabel m_afcKPLabel{"KP: ", this};
|
||||||
|
FSpinBox m_afcKP{this};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setText("Hotspot Gain & AFC Adjustment");
|
||||||
|
FDialog::setSize(FSize{50, 22});
|
||||||
|
|
||||||
|
AdjustWndBase::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initControls()
|
||||||
|
{
|
||||||
|
// gain
|
||||||
|
{
|
||||||
|
m_adjGainLabel.setGeometry(FPoint(2, 1), FSize(30, 2));
|
||||||
|
m_adjGainLabel.setEmphasis();
|
||||||
|
m_adjGainLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_gainButtonGroup.setGeometry(FPoint(2, 3), FSize(30, 6));
|
||||||
|
m_gainAHL.setPos(FPoint(1, 1));
|
||||||
|
m_gainAHL.addCallback("toggled", [&]() {
|
||||||
|
if (m_gainAHL.isChecked()) {
|
||||||
|
m_setup->m_modem->m_adfGainMode = ADF_GAIN_AUTO_LIN;
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_gainLow.setPos(FPoint(1, 2));
|
||||||
|
m_gainLow.addCallback("toggled", [&]() {
|
||||||
|
if (m_gainAuto.isChecked()) {
|
||||||
|
m_setup->m_modem->m_adfGainMode = ADF_GAIN_LOW;
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_gainHigh.setPos(FPoint(1, 3));
|
||||||
|
m_gainHigh.addCallback("toggled", [&]() {
|
||||||
|
if (m_gainAuto.isChecked()) {
|
||||||
|
m_setup->m_modem->m_adfGainMode = ADF_GAIN_HIGH;
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_gainAuto.setPos(FPoint(1, 4));
|
||||||
|
m_gainAuto.addCallback("toggled", [&]() {
|
||||||
|
if (m_gainAuto.isChecked()) {
|
||||||
|
m_setup->m_modem->m_adfGainMode = ADF_GAIN_AUTO;
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// afc
|
||||||
|
{
|
||||||
|
m_adjAFCLabel.setGeometry(FPoint(2, 10), FSize(30, 2));
|
||||||
|
m_adjAFCLabel.setEmphasis();
|
||||||
|
m_adjAFCLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_afcEnabled.setGeometry(FPoint(2, 12), FSize(10, 1));
|
||||||
|
m_afcEnabled.setChecked(m_setup->m_modem->m_afcEnable);
|
||||||
|
m_afcEnabled.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_modem->m_afcEnable = m_afcEnabled.isChecked();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_afcRangeLabel.setGeometry(FPoint(24, 12), FSize(10, 1));
|
||||||
|
m_afcRange.setGeometry(FPoint(33, 12), FSize(10, 1));
|
||||||
|
m_afcRange.setRange(0, 256);
|
||||||
|
m_afcRange.setValue(m_setup->m_modem->m_afcRange);
|
||||||
|
m_afcRange.setShadow(false);
|
||||||
|
m_afcRange.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_afcRange = m_afcRange.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_afcKILabel.setGeometry(FPoint(2, 13), FSize(10, 1));
|
||||||
|
m_afcKI.setGeometry(FPoint(10, 13), FSize(10, 1));
|
||||||
|
m_afcKI.setRange(0, 16);
|
||||||
|
m_afcKI.setValue(m_setup->m_modem->m_afcKI);
|
||||||
|
m_afcKI.setShadow(false);
|
||||||
|
m_afcKI.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_afcKI = m_afcKI.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_afcKPLabel.setGeometry(FPoint(24, 13), FSize(10, 1));
|
||||||
|
m_afcKP.setGeometry(FPoint(33, 13), FSize(10, 1));
|
||||||
|
m_afcKP.setRange(0, 8);
|
||||||
|
m_afcKP.setValue(m_setup->m_modem->m_afcKP);
|
||||||
|
m_afcKP.setShadow(false);
|
||||||
|
m_afcKP.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_afcKP = m_afcKP.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
AdjustWndBase::initControls();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __HS_GAIN_ADJUST_WND_H__
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,319 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__LEVEL_ADJUST_WND_H__)
|
||||||
|
#define __LEVEL_ADJUST_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include "host/setup/AdjustWndBase.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the modem level adjustment window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API LevelAdjustWnd final : public AdjustWndBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the LevelAdjustWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit LevelAdjustWnd(HostSetup* setup, FWidget* widget = nullptr) : AdjustWndBase{setup, widget}
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FLabel m_softwareLevelLabel{"Software Levels", this};
|
||||||
|
FLabel m_rxLevelLabel{"Rx Level: ", this};
|
||||||
|
FLabel m_rxDCOffsetLabel{"Rx DC Offset: ", this};
|
||||||
|
FLabel m_txLevelLabel{"Tx Level: ", this};
|
||||||
|
FLabel m_txDCOffsetLabel{"Tx DC Offset: ", this};
|
||||||
|
|
||||||
|
FLabel m_softpotLevelLabel{"Softpot Levels", this};
|
||||||
|
FLabel m_rxCoarseLabel{"Rx Coarse: ", this};
|
||||||
|
FLabel m_rxFineLabel{"Rx Fine: ", this};
|
||||||
|
FLabel m_txCoarseLabel{"Tx Coarse: ", this};
|
||||||
|
FLabel m_rssiCoarseLabel{"RSSI Coarse: ", this};
|
||||||
|
|
||||||
|
FLabel m_digitalTimingLabel{"Digital Timing", this};
|
||||||
|
FLabel m_fdmaPreambleLabel{"FDMA Preambles: ", this};
|
||||||
|
FLabel m_dmrRxDelayLabel{"DMR Rx Delay: ", this};
|
||||||
|
FLabel m_p25CorrCountLabel{"P25 Corr. Count: ", this};
|
||||||
|
|
||||||
|
FLabel m_freqAdjustLabel{"Hotspot Frequency Offset", this};
|
||||||
|
FLabel m_rxFreqAdjLabel{"Rx Freq. Offset: ", this};
|
||||||
|
FLabel m_txFreqAdjLabel{"Tx Freq. Offset: ", this};
|
||||||
|
|
||||||
|
FSpinBox m_rxLevel{this};
|
||||||
|
FSpinBox m_rxDCOffsetLevel{this};
|
||||||
|
FSpinBox m_txLevel{this};
|
||||||
|
FSpinBox m_txDCOffsetLevel{this};
|
||||||
|
|
||||||
|
FSpinBox m_rxCoarseLevel{this};
|
||||||
|
FSpinBox m_rxFineLevel{this};
|
||||||
|
FSpinBox m_txCoarseLevel{this};
|
||||||
|
FSpinBox m_rssiCoarseLevel{this};
|
||||||
|
|
||||||
|
FSpinBox m_fdmaPreambles{this};
|
||||||
|
FSpinBox m_dmrRxDelay{this};
|
||||||
|
FSpinBox m_p25CorrCount{this};
|
||||||
|
|
||||||
|
FSpinBox m_rxTuning{this};
|
||||||
|
FSpinBox m_txTuning{this};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setText("Modem Level Adjustment");
|
||||||
|
FDialog::setSize(FSize{65, 22});
|
||||||
|
|
||||||
|
AdjustWndBase::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initControls() override
|
||||||
|
{
|
||||||
|
// software levels
|
||||||
|
{
|
||||||
|
m_softwareLevelLabel.setGeometry(FPoint(2, 1), FSize(20, 2));
|
||||||
|
m_softwareLevelLabel.setEmphasis();
|
||||||
|
m_softwareLevelLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_rxLevelLabel.setGeometry(FPoint(2, 3), FSize(20, 1));
|
||||||
|
m_rxLevel.setGeometry(FPoint(18, 3), FSize(10, 1));
|
||||||
|
m_rxLevel.setRange(0, 100);
|
||||||
|
m_rxLevel.setValue(m_setup->m_modem->m_rxLevel);
|
||||||
|
m_rxLevel.setShadow(false);
|
||||||
|
m_rxLevel.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_rxLevel = m_rxLevel.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_rxDCOffsetLabel.setGeometry(FPoint(2, 4), FSize(20, 1));
|
||||||
|
m_rxDCOffsetLevel.setGeometry(FPoint(18, 4), FSize(10, 1));
|
||||||
|
m_rxDCOffsetLevel.setRange(-127, 127);
|
||||||
|
m_rxDCOffsetLevel.setValue(m_setup->m_modem->m_rxDCOffset);
|
||||||
|
m_rxDCOffsetLevel.setShadow(false);
|
||||||
|
m_rxDCOffsetLevel.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_rxDCOffset = m_rxDCOffsetLevel.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_txLevelLabel.setGeometry(FPoint(2, 5), FSize(20, 1));
|
||||||
|
m_txLevel.setGeometry(FPoint(18, 5), FSize(10, 1));
|
||||||
|
m_txLevel.setRange(0, 100);
|
||||||
|
m_txLevel.setValue(m_setup->m_modem->m_cwIdTXLevel);
|
||||||
|
m_txLevel.setShadow(false);
|
||||||
|
m_txLevel.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_cwIdTXLevel = m_txLevel.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_txDCOffsetLabel.setGeometry(FPoint(2, 6), FSize(20, 1));
|
||||||
|
m_txDCOffsetLevel.setGeometry(FPoint(18, 6), FSize(10, 1));
|
||||||
|
m_txDCOffsetLevel.setRange(-127, 127);
|
||||||
|
m_txDCOffsetLevel.setValue(m_setup->m_modem->m_txDCOffset);
|
||||||
|
m_txDCOffsetLevel.setShadow(false);
|
||||||
|
m_txDCOffsetLevel.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_txDCOffset = m_txDCOffsetLevel.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// digital timing
|
||||||
|
{
|
||||||
|
m_digitalTimingLabel.setGeometry(FPoint(32, 1), FSize(20, 2));
|
||||||
|
m_digitalTimingLabel.setEmphasis();
|
||||||
|
m_digitalTimingLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_fdmaPreambleLabel.setGeometry(FPoint(32, 3), FSize(20, 1));
|
||||||
|
m_fdmaPreambles.setGeometry(FPoint(52, 3), FSize(10, 1));
|
||||||
|
m_fdmaPreambles.setRange(0, 255);
|
||||||
|
m_fdmaPreambles.setValue(m_setup->m_modem->m_fdmaPreamble);
|
||||||
|
m_fdmaPreambles.setShadow(false);
|
||||||
|
m_fdmaPreambles.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_fdmaPreamble = m_fdmaPreambles.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_dmrRxDelayLabel.setGeometry(FPoint(32, 4), FSize(20, 1));
|
||||||
|
m_dmrRxDelay.setGeometry(FPoint(52, 4), FSize(10, 1));
|
||||||
|
m_dmrRxDelay.setRange(0, 255);
|
||||||
|
m_dmrRxDelay.setValue(m_setup->m_modem->m_dmrRxDelay);
|
||||||
|
m_dmrRxDelay.setShadow(false);
|
||||||
|
m_dmrRxDelay.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_dmrRxDelay = m_dmrRxDelay.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_p25CorrCountLabel.setGeometry(FPoint(32, 5), FSize(20, 1));
|
||||||
|
m_p25CorrCount.setGeometry(FPoint(52, 5), FSize(10, 1));
|
||||||
|
m_p25CorrCount.setRange(0, 255);
|
||||||
|
m_p25CorrCount.setValue(m_setup->m_modem->m_p25CorrCount);
|
||||||
|
m_p25CorrCount.setShadow(false);
|
||||||
|
m_p25CorrCount.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_p25CorrCount = m_p25CorrCount.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// softpot levels
|
||||||
|
{
|
||||||
|
m_softpotLevelLabel.setGeometry(FPoint(2, 8), FSize(20, 2));
|
||||||
|
m_softpotLevelLabel.setEmphasis();
|
||||||
|
m_softpotLevelLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_rxCoarseLabel.setGeometry(FPoint(2, 10), FSize(20, 1));
|
||||||
|
m_rxCoarseLevel.setGeometry(FPoint(18, 10), FSize(10, 1));
|
||||||
|
m_rxCoarseLevel.setRange(-127, 127);
|
||||||
|
m_rxCoarseLevel.setValue(m_setup->m_modem->m_rxCoarsePot);
|
||||||
|
m_rxCoarseLevel.setShadow(false);
|
||||||
|
m_rxCoarseLevel.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_rxCoarsePot = m_rxCoarseLevel.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_rxFineLabel.setGeometry(FPoint(2, 11), FSize(20, 1));
|
||||||
|
m_rxFineLevel.setGeometry(FPoint(18, 11), FSize(10, 1));
|
||||||
|
m_rxFineLevel.setRange(-127, 127);
|
||||||
|
m_rxFineLevel.setValue(m_setup->m_modem->m_rxFinePot);
|
||||||
|
m_rxFineLevel.setShadow(false);
|
||||||
|
m_rxFineLevel.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_rxFinePot = m_rxFineLevel.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_txCoarseLabel.setGeometry(FPoint(2, 12), FSize(20, 1));
|
||||||
|
m_txCoarseLevel.setGeometry(FPoint(18, 12), FSize(10, 1));
|
||||||
|
m_txCoarseLevel.setRange(-127, 127);
|
||||||
|
m_txCoarseLevel.setValue(m_setup->m_modem->m_txCoarsePot);
|
||||||
|
m_txCoarseLevel.setShadow(false);
|
||||||
|
m_txCoarseLevel.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_txCoarsePot = m_txCoarseLevel.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_rssiCoarseLabel.setGeometry(FPoint(2, 13), FSize(20, 1));
|
||||||
|
m_rssiCoarseLevel.setGeometry(FPoint(18, 13), FSize(10, 1));
|
||||||
|
m_rssiCoarseLevel.setRange(-127, 127);
|
||||||
|
m_rssiCoarseLevel.setValue(m_setup->m_modem->m_rssiCoarsePot);
|
||||||
|
m_rssiCoarseLevel.setShadow(false);
|
||||||
|
m_rssiCoarseLevel.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_rssiCoarsePot = m_rssiCoarseLevel.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// frequency offset
|
||||||
|
{
|
||||||
|
m_freqAdjustLabel.setGeometry(FPoint(32, 8), FSize(30, 2));
|
||||||
|
m_freqAdjustLabel.setEmphasis();
|
||||||
|
m_freqAdjustLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_rxFreqAdjLabel.setGeometry(FPoint(32, 10), FSize(20, 1));
|
||||||
|
m_rxTuning.setGeometry(FPoint(52, 10), FSize(10, 1));
|
||||||
|
m_rxTuning.setRange(-1000, 1000);
|
||||||
|
m_rxTuning.setValue(m_setup->m_modem->m_rxTuning);
|
||||||
|
m_rxTuning.setShadow(false);
|
||||||
|
m_rxTuning.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_rxTuning = m_rxTuning.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_txFreqAdjLabel.setGeometry(FPoint(32, 12), FSize(20, 1));
|
||||||
|
m_txTuning.setGeometry(FPoint(52, 12), FSize(10, 1));
|
||||||
|
m_txTuning.setRange(-1000, 1000);
|
||||||
|
m_txTuning.setValue(m_setup->m_modem->m_txTuning);
|
||||||
|
m_txTuning.setShadow(false);
|
||||||
|
m_txTuning.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_txTuning = m_txTuning.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup control states
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
if (m_setup->m_modem->m_isHotspot) {
|
||||||
|
m_p25CorrCount.setDisable();
|
||||||
|
|
||||||
|
m_rxLevel.setDisable();
|
||||||
|
m_rxDCOffsetLevel.setDisable();
|
||||||
|
m_txDCOffsetLevel.setDisable();
|
||||||
|
|
||||||
|
m_rxCoarseLevel.setDisable();
|
||||||
|
m_rxFineLevel.setDisable();
|
||||||
|
m_txCoarseLevel.setDisable();
|
||||||
|
m_rssiCoarseLevel.setDisable();
|
||||||
|
|
||||||
|
m_rxTuning.setEnable();
|
||||||
|
m_txTuning.setEnable();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_p25CorrCount.setEnable();
|
||||||
|
|
||||||
|
m_rxLevel.setEnable();
|
||||||
|
m_rxDCOffsetLevel.setEnable();
|
||||||
|
m_txDCOffsetLevel.setEnable();
|
||||||
|
|
||||||
|
m_rxCoarseLevel.setEnable();
|
||||||
|
m_rxFineLevel.setEnable();
|
||||||
|
m_txCoarseLevel.setEnable();
|
||||||
|
m_rssiCoarseLevel.setEnable();
|
||||||
|
|
||||||
|
m_rxTuning.setDisable();
|
||||||
|
m_txTuning.setDisable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AdjustWndBase::initControls();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __LEVEL_ADJUST_WND_H__
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__LOG_DISPLAY_WND_H__)
|
||||||
|
#define __LOG_DISPLAY_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the log display window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API LogDisplayWnd final : public finalcut::FDialog, public std::ostringstream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the LogDisplayWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit LogDisplayWnd(FWidget* widget = nullptr) : FDialog{widget}
|
||||||
|
{
|
||||||
|
m_scrollText.ignorePadding();
|
||||||
|
|
||||||
|
m_timerId = addTimer(250); // starts the timer every 250 milliseconds
|
||||||
|
}
|
||||||
|
/// <summary>Copy constructor.</summary>
|
||||||
|
LogDisplayWnd(const LogDisplayWnd&) = delete;
|
||||||
|
/// <summary>Move constructor.</summary>
|
||||||
|
LogDisplayWnd(LogDisplayWnd&&) noexcept = delete;
|
||||||
|
/// <summary>Finalizes an instance of the LogDisplayWnd class.</summary>
|
||||||
|
~LogDisplayWnd() noexcept override = default;
|
||||||
|
|
||||||
|
/// <summary>Disable copy assignment operator (=).</summary>
|
||||||
|
auto operator= (const LogDisplayWnd&) -> LogDisplayWnd& = delete;
|
||||||
|
/// <summary>Disable move assignment operator (=).</summary>
|
||||||
|
auto operator= (LogDisplayWnd&&) noexcept -> LogDisplayWnd& = delete;
|
||||||
|
|
||||||
|
/// <summary>Disable set X coordinate.</summary>
|
||||||
|
void setX(int, bool = true) override { }
|
||||||
|
/// <summary>Disable set Y coordinate.</summary>
|
||||||
|
void setY(int, bool = true) override { }
|
||||||
|
/// <summary>Disable set position.</summary>
|
||||||
|
void setPos(const FPoint&, bool = true) override { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FTextView m_scrollText{this};
|
||||||
|
int m_timerId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
|
auto lightning = "\u26a1";
|
||||||
|
FDialog::setText("System Log"s + lightning);
|
||||||
|
|
||||||
|
std::size_t maxWidth;
|
||||||
|
const auto& rootWidget = getRootWidget();
|
||||||
|
|
||||||
|
if (rootWidget) {
|
||||||
|
maxWidth = rootWidget->getClientWidth() - 3;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// fallback to xterm default size
|
||||||
|
maxWidth = 77;
|
||||||
|
}
|
||||||
|
|
||||||
|
FDialog::setGeometry(FPoint{2, 2}, FSize{maxWidth, 20});
|
||||||
|
FDialog::setMinimumSize(FSize{80, 5});
|
||||||
|
FDialog::setResizeable(false);
|
||||||
|
FDialog::setMinimizable(false);
|
||||||
|
FDialog::setTitlebarButtonVisibility(false);
|
||||||
|
FDialog::setShadow();
|
||||||
|
|
||||||
|
m_scrollText.setGeometry(FPoint{1, 2}, FSize{getWidth(), getHeight() - 1});
|
||||||
|
|
||||||
|
FDialog::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Event Handlers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timer"></param>
|
||||||
|
void onTimer(FTimerEvent* timer) override
|
||||||
|
{
|
||||||
|
if (timer != nullptr) {
|
||||||
|
if (timer->getTimerId() == m_timerId) {
|
||||||
|
if (str().empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_scrollText.append(str());
|
||||||
|
str("");
|
||||||
|
m_scrollText.scrollToEnd();
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __LOG_DISPLAY_WND_H__
|
||||||
@ -0,0 +1,170 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__LOGGING_AND_DATA_SET_WND_H__)
|
||||||
|
#define __LOGGING_AND_DATA_SET_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include "host/setup/CloseWndBase.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the logging and data configuration window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API LoggingAndDataSetWnd final : public CloseWndBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the LoggingAndDataSetWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit LoggingAndDataSetWnd(HostSetup* setup, FWidget* widget = nullptr) : CloseWndBase{setup, widget}
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FLabel m_loggingLabel{"Logging", this};
|
||||||
|
FLabel m_dataLabel{"Data Paths", this};
|
||||||
|
|
||||||
|
FLabel m_logFilePathLabel{"Log File Path: ", this};
|
||||||
|
FLineEdit m_logFilePath{this};
|
||||||
|
FLabel m_actFilePathLabel{"Activity File Path: ", this};
|
||||||
|
FLineEdit m_actFilePath{this};
|
||||||
|
FLabel m_logLevelLabel{"Logging Level (1-6 lowest): ", this};
|
||||||
|
FSpinBox m_logLevel{this};
|
||||||
|
|
||||||
|
FLabel m_chIdTablePathLabel{"Ch. Identity Table File Path: ", this};
|
||||||
|
FLineEdit m_chIdTablePath{this};
|
||||||
|
FLabel m_radioIdPathLabel{"Radio ID ACL File Path: ", this};
|
||||||
|
FLineEdit m_radioIdPath{this};
|
||||||
|
FLabel m_tgIdPathLabel{"Talkgroup ACL File Path: ", this};
|
||||||
|
FLineEdit m_tgIdPath{this};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setText("Logging and Data Configuration");
|
||||||
|
FDialog::setSize(FSize{68, 19});
|
||||||
|
|
||||||
|
m_enableSetButton = false;
|
||||||
|
CloseWndBase::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initControls()
|
||||||
|
{
|
||||||
|
yaml::Node logConf = m_setup->m_conf["log"];
|
||||||
|
uint32_t logLevel = logConf["fileLevel"].as<uint32_t>(1U);
|
||||||
|
std::string logFilePath = logConf["filePath"].as<std::string>();
|
||||||
|
std::string logActFilePath = logConf["activityFilePath"].as<std::string>();
|
||||||
|
|
||||||
|
// logging
|
||||||
|
{
|
||||||
|
m_loggingLabel.setGeometry(FPoint(2, 1), FSize(20, 2));
|
||||||
|
m_loggingLabel.setEmphasis();
|
||||||
|
m_loggingLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_logFilePathLabel.setGeometry(FPoint(2, 3), FSize(30, 1));
|
||||||
|
m_logFilePath.setGeometry(FPoint(33, 3), FSize(32, 1));
|
||||||
|
m_logFilePath.setText(logFilePath);
|
||||||
|
m_logFilePath.setShadow(false);
|
||||||
|
m_logFilePath.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["log"]["filePath"] = m_logFilePath.getText().toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_actFilePathLabel.setGeometry(FPoint(2, 4), FSize(30, 1));
|
||||||
|
m_actFilePath.setGeometry(FPoint(33, 4), FSize(32, 1));
|
||||||
|
m_actFilePath.setText(logActFilePath);
|
||||||
|
m_actFilePath.setShadow(false);
|
||||||
|
m_actFilePath.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["log"]["activityFilePath"] = m_actFilePath.getText().toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_logLevelLabel.setGeometry(FPoint(2, 5), FSize(30, 1));
|
||||||
|
m_logLevel.setGeometry(FPoint(33, 5), FSize(10, 1));
|
||||||
|
m_logLevel.setRange(1, 6);
|
||||||
|
m_logLevel.setValue(logLevel);
|
||||||
|
m_logLevel.setShadow(false);
|
||||||
|
m_logLevel.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["log"]["displayLevel"] = __INT_STR(m_logLevel.getValue());
|
||||||
|
m_setup->m_conf["log"]["fileLevel"] = __INT_STR(m_logLevel.getValue());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
yaml::Node idenTable = m_setup->m_conf["system"]["iden_table"];
|
||||||
|
std::string idenFilePath = idenTable["file"].as<std::string>();
|
||||||
|
yaml::Node radioId = m_setup->m_conf["system"]["radio_id"];
|
||||||
|
std::string ridFilePath = radioId["file"].as<std::string>();
|
||||||
|
yaml::Node talkgroupId = m_setup->m_conf["system"]["talkgroup_id"];
|
||||||
|
std::string tgidFilePath = talkgroupId["file"].as<std::string>();
|
||||||
|
|
||||||
|
// data paths
|
||||||
|
{
|
||||||
|
m_dataLabel.setGeometry(FPoint(2, 7), FSize(20, 2));
|
||||||
|
m_dataLabel.setEmphasis();
|
||||||
|
m_dataLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_chIdTablePathLabel.setGeometry(FPoint(2, 9), FSize(30, 1));
|
||||||
|
m_chIdTablePath.setGeometry(FPoint(33, 9), FSize(32, 1));
|
||||||
|
m_chIdTablePath.setText(idenFilePath);
|
||||||
|
m_chIdTablePath.setShadow(false);
|
||||||
|
m_chIdTablePath.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["iden_table"]["file"] = m_chIdTablePath.getText().toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_radioIdPathLabel.setGeometry(FPoint(2, 10), FSize(30, 1));
|
||||||
|
m_radioIdPath.setGeometry(FPoint(33, 10), FSize(32, 1));
|
||||||
|
m_radioIdPath.setText(ridFilePath);
|
||||||
|
m_radioIdPath.setShadow(false);
|
||||||
|
m_radioIdPath.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["radio_id"]["file"] = m_radioIdPath.getText().toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_tgIdPathLabel.setGeometry(FPoint(2, 11), FSize(30, 1));
|
||||||
|
m_tgIdPath.setGeometry(FPoint(33, 11), FSize(32, 1));
|
||||||
|
m_tgIdPath.setText(tgidFilePath);
|
||||||
|
m_tgIdPath.setShadow(false);
|
||||||
|
m_tgIdPath.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["talkgroup_id"]["file"] = m_tgIdPath.getText().toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseWndBase::initControls();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __LOGGING_AND_DATA_SET_WND_H__
|
||||||
@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__MODEM_STATUS_WND_H__)
|
||||||
|
#define __MODEM_STATUS_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the modem status display window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API ModemStatusWnd final : public finalcut::FDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the ModemStatusWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit ModemStatusWnd(FWidget* widget = nullptr) : FDialog{widget}
|
||||||
|
{
|
||||||
|
m_scrollText.ignorePadding();
|
||||||
|
}
|
||||||
|
/// <summary>Copy constructor.</summary>
|
||||||
|
ModemStatusWnd(const ModemStatusWnd&) = delete;
|
||||||
|
/// <summary>Move constructor.</summary>
|
||||||
|
ModemStatusWnd(ModemStatusWnd&&) noexcept = delete;
|
||||||
|
/// <summary>Finalizes an instance of the ModemStatusWnd class.</summary>
|
||||||
|
~ModemStatusWnd() noexcept override = default;
|
||||||
|
|
||||||
|
/// <summary>Disable copy assignment operator (=).</summary>
|
||||||
|
auto operator= (const ModemStatusWnd&) -> ModemStatusWnd& = delete;
|
||||||
|
/// <summary>Disable move assignment operator (=).</summary>
|
||||||
|
auto operator= (ModemStatusWnd&&) noexcept -> ModemStatusWnd& = delete;
|
||||||
|
|
||||||
|
/// <summary>Disable set X coordinate.</summary>
|
||||||
|
void setX(int, bool = true) override { }
|
||||||
|
/// <summary>Disable set Y coordinate.</summary>
|
||||||
|
void setY(int, bool = true) override { }
|
||||||
|
/// <summary>Disable set position.</summary>
|
||||||
|
void setPos(const FPoint&, bool = true) override { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to append text.
|
||||||
|
/// </summary>
|
||||||
|
void append(std::string str)
|
||||||
|
{
|
||||||
|
if (str.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_scrollText.append(str);
|
||||||
|
m_scrollText.scrollToEnd();
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to clear the text.
|
||||||
|
/// </summary>
|
||||||
|
void clear() { m_scrollText.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FTextView m_scrollText{this};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setText("Modem Status (update every 1s)");
|
||||||
|
|
||||||
|
std::size_t maxWidth;
|
||||||
|
const auto& rootWidget = getRootWidget();
|
||||||
|
|
||||||
|
if (rootWidget) {
|
||||||
|
maxWidth = rootWidget->getClientWidth() - 3;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// fallback to xterm default size
|
||||||
|
maxWidth = 77;
|
||||||
|
}
|
||||||
|
|
||||||
|
FDialog::setGeometry(FPoint{2, 23}, FSize{maxWidth, 25});
|
||||||
|
FDialog::setMinimumSize(FSize{80, 5});
|
||||||
|
FDialog::setResizeable(false);
|
||||||
|
FDialog::setMinimizable(false);
|
||||||
|
FDialog::setTitlebarButtonVisibility(false);
|
||||||
|
FDialog::setShadow();
|
||||||
|
|
||||||
|
m_scrollText.setGeometry(FPoint{1, 2}, FSize{getWidth(), getHeight() - 1});
|
||||||
|
|
||||||
|
FDialog::initLayout();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __MODEM_STATUS_WND_H__
|
||||||
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__SETUP_APPLICATION_H__)
|
||||||
|
#define __SETUP_APPLICATION_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "host/setup/SetupMainWnd.h"
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the finalcut application.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API SetupApplication final : public finalcut::FApplication {
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the SetupWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="argc"></param>
|
||||||
|
/// <param name="argv"></param>
|
||||||
|
explicit SetupApplication(HostSetup* setup, const int& argc, char** argv) : FApplication{argc, argv},
|
||||||
|
m_setup(setup)
|
||||||
|
{
|
||||||
|
m_statusRefreshTimer = addTimer(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
virtual void processExternalUserEvent()
|
||||||
|
{
|
||||||
|
if (m_setup->m_p25TduTest && m_setup->m_queue.hasSpace(p25::P25_TDU_FRAME_LENGTH_BYTES + 2U)) {
|
||||||
|
uint8_t data[p25::P25_TDU_FRAME_LENGTH_BYTES + 2U];
|
||||||
|
::memset(data + 2U, 0x00U, p25::P25_TDU_FRAME_LENGTH_BYTES);
|
||||||
|
|
||||||
|
// Generate Sync
|
||||||
|
p25::Sync::addP25Sync(data + 2U);
|
||||||
|
|
||||||
|
// Generate NID
|
||||||
|
std::unique_ptr<p25::NID> nid = new_unique(p25::NID, 1U);
|
||||||
|
nid->encode(data + 2U, p25::P25_DUID_TDU);
|
||||||
|
|
||||||
|
// Add busy bits
|
||||||
|
p25::P25Utils::addBusyBits(data + 2U, p25::P25_TDU_FRAME_LENGTH_BITS, true, true);
|
||||||
|
|
||||||
|
data[0U] = modem::TAG_EOT;
|
||||||
|
data[1U] = 0x00U;
|
||||||
|
|
||||||
|
m_setup->addFrame(data, p25::P25_TDU_FRAME_LENGTH_BYTES + 2U, p25::P25_LDU_FRAME_LENGTH_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// -- Modem Clocking --
|
||||||
|
// ------------------------------------------------------
|
||||||
|
|
||||||
|
uint32_t ms = m_setup->m_stopWatch.elapsed();
|
||||||
|
m_setup->m_stopWatch.start();
|
||||||
|
|
||||||
|
m_setup->m_modem->clock(ms);
|
||||||
|
|
||||||
|
m_setup->timerClock();
|
||||||
|
|
||||||
|
if (ms < 2U)
|
||||||
|
Thread::sleep(1U);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Event Handlers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timer"></param>
|
||||||
|
void onTimer(FTimerEvent* timer) override
|
||||||
|
{
|
||||||
|
if (timer != nullptr) {
|
||||||
|
if (timer->getTimerId() == m_statusRefreshTimer) {
|
||||||
|
m_setup->m_setupWnd->updateMenuStates();
|
||||||
|
|
||||||
|
// display modem status
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
if (m_setup->m_setupWnd->m_statusWnd.isShown()) {
|
||||||
|
m_setup->printStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
HostSetup* m_setup;
|
||||||
|
|
||||||
|
int m_statusRefreshTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SETUP_APPLICATION_H__
|
||||||
@ -0,0 +1,761 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__SETUP_WND_H__)
|
||||||
|
#define __SETUP_WND_H__
|
||||||
|
|
||||||
|
#include "modem/Modem.h"
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
using namespace modem;
|
||||||
|
|
||||||
|
#include "host/setup/LogDisplayWnd.h"
|
||||||
|
#include "host/setup/ModemStatusWnd.h"
|
||||||
|
#include "host/setup/BERDisplayWnd.h"
|
||||||
|
|
||||||
|
#include "host/setup/LevelAdjustWnd.h"
|
||||||
|
#include "host/setup/SymbLevelAdjustWnd.h"
|
||||||
|
#include "host/setup/HSBandwidthAdjustWnd.h"
|
||||||
|
#include "host/setup/HSGainAdjustWnd.h"
|
||||||
|
#include "host/setup/FIFOBufferAdjustWnd.h"
|
||||||
|
|
||||||
|
#include "host/setup/LoggingAndDataSetWnd.h"
|
||||||
|
#include "host/setup/SystemConfigSetWnd.h"
|
||||||
|
#include "host/setup/SiteParamSetWnd.h"
|
||||||
|
#include "host/setup/ChannelConfigSetWnd.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Prototypes
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API SetupApplication;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the root window control.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API SetupMainWnd final : public finalcut::FWidget {
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the SetupMainWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit SetupMainWnd(HostSetup* setup, FWidget* widget = nullptr) : FWidget{widget},
|
||||||
|
m_setup(setup)
|
||||||
|
{
|
||||||
|
__InternalOutputStream(m_logWnd);
|
||||||
|
m_statusWnd.hide();
|
||||||
|
|
||||||
|
resetBERWnd();
|
||||||
|
|
||||||
|
// file menu
|
||||||
|
m_fileMenuSeparator1.setSeparator();
|
||||||
|
m_fileMenuSeparator2.setSeparator();
|
||||||
|
m_connectToModemItem.addAccelerator(FKey::Meta_c); // Meta/Alt + C
|
||||||
|
m_connectToModemItem.addCallback("clicked", this, &SetupMainWnd::cb_connectToModemClick);
|
||||||
|
m_keyF8.addCallback("activate", this, &SetupMainWnd::cb_connectToModemClick);
|
||||||
|
m_saveSettingsItem.addAccelerator(FKey::Meta_s); // Meta/Alt + S
|
||||||
|
m_saveSettingsItem.addCallback("clicked", this, [&]() { m_setup->saveConfig(); });
|
||||||
|
m_keyF2.addCallback("activate", this, [&]() { m_setup->saveConfig(); });
|
||||||
|
m_quitItem.addAccelerator(FKey::Meta_x); // Meta/Alt + X
|
||||||
|
m_quitItem.addCallback("clicked", getFApplication(), &FApplication::cb_exitApp, this);
|
||||||
|
m_keyF3.addCallback("activate", getFApplication(), &FApplication::cb_exitApp, this);
|
||||||
|
m_keyF12.addCallback("activate", this, [&]() {
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
if (!m_setup->setTransmit()) {
|
||||||
|
FMessageBox::error(this, "Failed to enable modem transmit!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// setup menu
|
||||||
|
m_setupMenuSeparator1.setSeparator();
|
||||||
|
m_setupMenuSeparator2.setSeparator();
|
||||||
|
m_setLoggingDataConfig.addCallback("clicked", this, [&]() {
|
||||||
|
FMessageBox::error(this, "NOTE: These settings will take effect on restart of dvmhost.");
|
||||||
|
LoggingAndDataSetWnd wnd{m_setup, this};
|
||||||
|
wnd.show();
|
||||||
|
});
|
||||||
|
m_systemConfig.addCallback("clicked", this, [&]() {
|
||||||
|
SystemConfigSetWnd wnd{m_setup, this};
|
||||||
|
wnd.show();
|
||||||
|
});
|
||||||
|
m_siteParams.addCallback("clicked", this, [&]() {
|
||||||
|
SiteParamSetWnd wnd{m_setup, this};
|
||||||
|
wnd.show();
|
||||||
|
});
|
||||||
|
m_chConfig.addCallback("clicked", this, [&]() {
|
||||||
|
ChannelConfigSetWnd wnd{m_setup, this};
|
||||||
|
wnd.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
// calibrate menu
|
||||||
|
m_calibrateMenuSeparator1.setSeparator();
|
||||||
|
m_calibrateMenuSeparator2.setSeparator();
|
||||||
|
m_dmrCal.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_DMR_CAL;
|
||||||
|
m_setup->m_modeStr = DMR_CAL_STR;
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_dmrRx1K = false;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_p25Rx1K = false;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd();
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
m_p25Cal.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_P25_CAL;
|
||||||
|
m_setup->m_modeStr = P25_CAL_STR;
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_dmrRx1K = false;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_p25Rx1K = false;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd();
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
m_dmrLFCal.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_DMR_LF_CAL;
|
||||||
|
m_setup->m_modeStr = DMR_LF_CAL_STR;
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_dmrRx1K = false;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_p25Rx1K = false;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd();
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
m_dmrCal1K.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_DMR_CAL_1K;
|
||||||
|
m_setup->m_modeStr = DMR_CAL_1K_STR;
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_dmrRx1K = false;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_p25Rx1K = false;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd();
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
m_dmrDMOCal1K.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_DMR_DMO_CAL_1K;
|
||||||
|
m_setup->m_modeStr = DMR_DMO_CAL_1K_STR;
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_dmrRx1K = false;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_p25Rx1K = false;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd();
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
m_p25Cal1K.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_P25_CAL_1K;
|
||||||
|
m_setup->m_modeStr = P25_CAL_1K_STR;
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_dmrRx1K = false;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_p25Rx1K = false;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd();
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
m_p25TDUTest.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_P25;
|
||||||
|
m_setup->m_modeStr = P25_TDU_TEST_STR;
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_p25Enabled = true;
|
||||||
|
m_setup->m_p25TduTest = true;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd();
|
||||||
|
|
||||||
|
m_setup->m_queue.clear();
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
m_nxdnCal1K.addCallback("toggled", [&]() {
|
||||||
|
// are we on a protocol version 3 firmware?
|
||||||
|
if (m_setup->m_modem->getVersion() >= 3U) {
|
||||||
|
m_setup->m_mode = STATE_NXDN_CAL;
|
||||||
|
m_setup->m_modeStr = NXDN_CAL_1K_STR;
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_dmrRx1K = false;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_p25Rx1K = false;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd();
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FMessageBox::error(this, NXDN_CAL_1K_STR " test mode is not supported on your firmware!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_dmrFEC.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_DMR;
|
||||||
|
m_setup->m_modeStr = DMR_FEC_STR;
|
||||||
|
m_setup->m_dmrRx1K = false;
|
||||||
|
/*
|
||||||
|
if (c == ')') {
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
} else {
|
||||||
|
m_setup->m_duplex = false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
m_setup->m_duplex = false;
|
||||||
|
m_setup->m_dmrEnabled = true;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd(true);
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
m_dmrFEC1K.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_DMR;
|
||||||
|
m_setup->m_modeStr = DMR_FEC_1K_STR;
|
||||||
|
m_setup->m_dmrRx1K = true;
|
||||||
|
/*
|
||||||
|
if (c == ')') {
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
} else {
|
||||||
|
m_setup->m_duplex = false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
m_setup->m_duplex = false;
|
||||||
|
m_setup->m_dmrEnabled = true;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd(true);
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
m_p25FEC.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_P25;
|
||||||
|
m_setup->m_modeStr = P25_FEC_STR;
|
||||||
|
m_setup->m_p25Rx1K = false;
|
||||||
|
m_setup->m_duplex = false;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_p25Enabled = true;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd(true);
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
m_p25FEC1K.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_P25;
|
||||||
|
m_setup->m_modeStr = P25_FEC_1K_STR;
|
||||||
|
m_setup->m_p25Rx1K = true;
|
||||||
|
m_setup->m_duplex = false;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_p25Enabled = true;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
|
||||||
|
resetBERWnd(true);
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
m_nxdnFEC.addCallback("toggled", [&]() {
|
||||||
|
// are we on a protocol version 3 firmware?
|
||||||
|
if (m_setup->m_modem->getVersion() >= 3U) {
|
||||||
|
m_setup->m_mode = STATE_NXDN;
|
||||||
|
m_setup->m_modeStr = NXDN_FEC_STR;
|
||||||
|
m_setup->m_duplex = false;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_p25Rx1K = false;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
m_setup->m_nxdnEnabled = true;
|
||||||
|
|
||||||
|
resetBERWnd(true);
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FMessageBox::error(this, NXDN_FEC_STR " test mode is not supported on your firmware!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_rssiCal.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_mode = STATE_RSSI_CAL;
|
||||||
|
m_setup->m_modeStr = RSSI_CAL_STR;
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_dmrRx1K = false;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_p25Rx1K = false;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
|
||||||
|
resetBERWnd();
|
||||||
|
|
||||||
|
LogMessage(LOG_CAL, " - %s", m_setup->m_modeStr.c_str());
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_toggleTxInvert.addCallback("toggled", this, [&]() {
|
||||||
|
if (!m_setup->m_isHotspot) {
|
||||||
|
m_setup->m_modem->m_txInvert = !m_setup->m_modem->m_txInvert;
|
||||||
|
LogMessage(LOG_CAL, "Tx Invert: %s", m_setup->m_modem->m_txInvert ? "on" : "off");
|
||||||
|
m_setup->writeConfig();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_toggleRxInvert.addCallback("toggled", this, [&]() {
|
||||||
|
if (!m_setup->m_isHotspot) {
|
||||||
|
m_setup->m_modem->m_rxInvert = !m_setup->m_modem->m_rxInvert;
|
||||||
|
LogMessage(LOG_CAL, "Rx Invert: %s", m_setup->m_modem->m_rxInvert ? "on" : "off");
|
||||||
|
m_setup->writeConfig();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_togglePTTInvert.addCallback("toggled", this, [&]() {
|
||||||
|
if (!m_setup->m_isHotspot) {
|
||||||
|
m_setup->m_modem->m_pttInvert = !m_setup->m_modem->m_pttInvert;
|
||||||
|
LogMessage(LOG_CAL, "PTT Invert: %s", m_setup->m_modem->m_pttInvert ? "on" : "off");
|
||||||
|
m_setup->writeConfig();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_toggleDCBlocker.addCallback("toggled", this, [&]() {
|
||||||
|
if (!m_setup->m_isHotspot) {
|
||||||
|
m_setup->m_modem->m_dcBlocker = !m_setup->m_modem->m_dcBlocker;
|
||||||
|
LogMessage(LOG_CAL, "DC Blocker: %s", m_setup->m_modem->m_dcBlocker ? "on" : "off");
|
||||||
|
m_setup->writeConfig();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_adjustLevel.addAccelerator(FKey::Meta_l); // Meta/Alt + L
|
||||||
|
m_adjustLevel.addCallback("clicked", this, [&]() {
|
||||||
|
LevelAdjustWnd wnd{m_setup, this};
|
||||||
|
wnd.show();
|
||||||
|
});
|
||||||
|
m_keyF5.addCallback("activate", this, [&]() {
|
||||||
|
LevelAdjustWnd wnd{m_setup, this};
|
||||||
|
wnd.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
// engineering menu
|
||||||
|
m_engineeringMenuSeparator1.setSeparator();
|
||||||
|
m_engineeringMenuSeparator2.setSeparator();
|
||||||
|
m_engineeringMenuSeparator3.setSeparator();
|
||||||
|
m_adjSymLevel.addAccelerator(FKey::Meta_s); // Meta/Alt + S
|
||||||
|
m_adjSymLevel.addCallback("clicked", this, [&]() {
|
||||||
|
SymbLevelAdjustWnd wnd{m_setup, this};
|
||||||
|
wnd.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_adjFifoBuffers.addAccelerator(FKey::Meta_f); // Meta/Alt + F
|
||||||
|
m_adjFifoBuffers.addCallback("clicked", this, [&]() {
|
||||||
|
FIFOBufferAdjustWnd wnd{m_setup, this};
|
||||||
|
wnd.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_adjHSBandwidth.addAccelerator(FKey::Meta_b); // Meta/Alt + B
|
||||||
|
m_adjHSBandwidth.addCallback("clicked", this, [&]() {
|
||||||
|
HSBandwidthAdjustWnd wnd{m_setup, this};
|
||||||
|
wnd.show();
|
||||||
|
});
|
||||||
|
m_adjHSGain.addAccelerator(FKey::Meta_g); // Meta/Alt + G
|
||||||
|
m_adjHSGain.addCallback("clicked", this, [&]() {
|
||||||
|
HSGainAdjustWnd wnd{m_setup, this};
|
||||||
|
wnd.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_eraseConfigArea.addCallback("clicked", this, [&]() {
|
||||||
|
FMessageBox wait("", L"Wait...",
|
||||||
|
FMessageBox::ButtonType::Reject, FMessageBox::ButtonType::Reject, FMessageBox::ButtonType::Reject, this);
|
||||||
|
wait.setCenterText();
|
||||||
|
wait.setModal(false);
|
||||||
|
wait.show();
|
||||||
|
|
||||||
|
m_setup->eraseFlash();
|
||||||
|
wait.hide();
|
||||||
|
});
|
||||||
|
m_readConfigArea.addCallback("clicked", this, [&]() {
|
||||||
|
FMessageBox wait("", L"Wait...",
|
||||||
|
FMessageBox::ButtonType::Reject, FMessageBox::ButtonType::Reject, FMessageBox::ButtonType::Reject, this);
|
||||||
|
wait.setCenterText();
|
||||||
|
wait.setModal(false);
|
||||||
|
wait.show();
|
||||||
|
|
||||||
|
m_setup->m_updateConfigFromModem = true;
|
||||||
|
m_setup->readFlash();
|
||||||
|
wait.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_forceHotspot.addCallback("toggled", this, [&]() {
|
||||||
|
if (m_setup->m_isConnected && !m_setup->m_isHotspot && m_forceHotspot.isChecked()) {
|
||||||
|
FMessageBox::error(this, "NOTICE: Enabling hotspot options on non-hotspot modems may result in undesired operation.");
|
||||||
|
}
|
||||||
|
m_setup->m_isHotspot = m_forceHotspot.isChecked();
|
||||||
|
m_setup->m_modem->m_forceHotspot = m_forceHotspot.isChecked();
|
||||||
|
m_setup->m_modem->m_isHotspot = m_forceHotspot.isChecked();
|
||||||
|
setMenuStates();
|
||||||
|
});
|
||||||
|
m_modemDebug.addCallback("toggled", this, [&]() {
|
||||||
|
m_setup->m_modem->m_debug = m_modemDebug.isChecked();
|
||||||
|
m_setup->m_debug = m_modemDebug.isChecked();
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
// help menu
|
||||||
|
m_aboutItem.addCallback("clicked", this, [&]() {
|
||||||
|
const FString line(2, UniChar::BoxDrawingsHorizontal);
|
||||||
|
FMessageBox info("About", line + __PROG_NAME__ + line + L"\n\n"
|
||||||
|
L"Version " + __VER__ + L"\n\n"
|
||||||
|
L"Copyright (c) 2017-2023 Bryan Biedenkapp, N2PLL and DVMProject (https://github.com/dvmproject) Authors." + L"\n"
|
||||||
|
L"Portions Copyright (c) 2015-2021 by Jonathan Naylor, G4KLX and others",
|
||||||
|
FMessageBox::ButtonType::Ok, FMessageBox::ButtonType::Reject, FMessageBox::ButtonType::Reject, this);
|
||||||
|
info.setCenterText();
|
||||||
|
info.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to set menu states.
|
||||||
|
/// </summary>
|
||||||
|
void setMenuStates()
|
||||||
|
{
|
||||||
|
m_dmrCal.setChecked();
|
||||||
|
if (m_setup->m_modem->m_debug) {
|
||||||
|
m_modemDebug.setChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_setup->m_modem->m_txInvert) {
|
||||||
|
m_toggleTxInvert.setChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_setup->m_modem->m_rxInvert) {
|
||||||
|
m_toggleRxInvert.setChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_setup->m_modem->m_pttInvert) {
|
||||||
|
m_togglePTTInvert.setChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_setup->m_modem->m_dcBlocker) {
|
||||||
|
m_toggleDCBlocker.setChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMenuStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to update menu states.
|
||||||
|
/// </summary>
|
||||||
|
void updateMenuStates()
|
||||||
|
{
|
||||||
|
if (!m_setup->m_isConnected) {
|
||||||
|
m_eraseConfigArea.setDisable();
|
||||||
|
m_readConfigArea.setDisable();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_eraseConfigArea.setEnable();
|
||||||
|
m_readConfigArea.setEnable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
if (m_setup->m_isHotspot) {
|
||||||
|
m_toggleTxInvert.setDisable();
|
||||||
|
m_toggleRxInvert.setDisable();
|
||||||
|
m_togglePTTInvert.setDisable();
|
||||||
|
m_toggleDCBlocker.setDisable();
|
||||||
|
|
||||||
|
m_adjSymLevel.setDisable();
|
||||||
|
m_adjHSBandwidth.setEnable();
|
||||||
|
m_adjHSGain.setEnable();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_toggleTxInvert.setEnable();
|
||||||
|
m_toggleRxInvert.setEnable();
|
||||||
|
m_togglePTTInvert.setEnable();
|
||||||
|
m_toggleDCBlocker.setEnable();
|
||||||
|
|
||||||
|
m_adjSymLevel.setEnable();
|
||||||
|
m_adjHSBandwidth.setDisable();
|
||||||
|
m_adjHSGain.setDisable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the instance of HostSetup.</summary>
|
||||||
|
const HostSetup* setup() { return m_setup; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class HostSetup;
|
||||||
|
friend class SetupApplication;
|
||||||
|
HostSetup* m_setup;
|
||||||
|
|
||||||
|
LogDisplayWnd m_logWnd{this};
|
||||||
|
ModemStatusWnd m_statusWnd{this};
|
||||||
|
BERDisplayWnd m_berWnd{this};
|
||||||
|
|
||||||
|
FString m_line{13, UniChar::BoxDrawingsHorizontal};
|
||||||
|
|
||||||
|
FMenuBar m_menuBar{this};
|
||||||
|
|
||||||
|
FMenu m_fileMenu{"&File", &m_menuBar};
|
||||||
|
FMenuItem m_connectToModemItem{"&Connect to Modem", &m_fileMenu};
|
||||||
|
FMenuItem m_fileMenuSeparator1{&m_fileMenu};
|
||||||
|
FMenuItem m_saveSettingsItem{"&Save Settings", &m_fileMenu};
|
||||||
|
FCheckMenuItem m_saveOnCloseToggle{"Save on Close?", &m_fileMenu};
|
||||||
|
FMenuItem m_fileMenuSeparator2{&m_fileMenu};
|
||||||
|
FMenuItem m_quitItem{"&Quit", &m_fileMenu};
|
||||||
|
|
||||||
|
FMenu m_setupMenu{"&Setup", &m_menuBar};
|
||||||
|
FMenuItem m_setLoggingDataConfig{"&Logging & Data Configuration", &m_setupMenu};
|
||||||
|
FMenuItem m_setupMenuSeparator1{&m_setupMenu};
|
||||||
|
FMenuItem m_systemConfig{"&System Configuration", &m_setupMenu};
|
||||||
|
FMenuItem m_siteParams{"Site &Parameters", &m_setupMenu};
|
||||||
|
FMenuItem m_setupMenuSeparator2{&m_setupMenu};
|
||||||
|
FMenuItem m_chConfig{"C&hannel Configuration", &m_setupMenu};
|
||||||
|
|
||||||
|
FMenu m_calibrateMenu{"&Calibrate", &m_menuBar};
|
||||||
|
FMenu m_opMode{"Operational Mode", &m_calibrateMenu};
|
||||||
|
FRadioMenuItem m_dmrCal{DMR_CAL_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_p25Cal{P25_CAL_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_dmrLFCal{DMR_LF_CAL_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_dmrCal1K{DMR_CAL_1K_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_dmrDMOCal1K{DMR_DMO_CAL_1K_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_p25Cal1K{P25_CAL_1K_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_p25TDUTest{P25_TDU_TEST_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_nxdnCal1K{NXDN_CAL_1K_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_dmrFEC{DMR_FEC_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_dmrFEC1K{DMR_FEC_1K_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_p25FEC{P25_FEC_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_p25FEC1K{P25_FEC_1K_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_nxdnFEC{NXDN_FEC_STR, &m_opMode};
|
||||||
|
FRadioMenuItem m_rssiCal{RSSI_CAL_STR, &m_opMode};
|
||||||
|
FMenuItem m_calibrateMenuSeparator1{&m_calibrateMenu};
|
||||||
|
FCheckMenuItem m_toggleTxInvert{"Transmit Invert", &m_calibrateMenu};
|
||||||
|
FCheckMenuItem m_toggleRxInvert{"Receive Invert", &m_calibrateMenu};
|
||||||
|
FCheckMenuItem m_togglePTTInvert{"PTT Invert", &m_calibrateMenu};
|
||||||
|
FCheckMenuItem m_toggleDCBlocker{"DC Blocker", &m_calibrateMenu};
|
||||||
|
FMenuItem m_calibrateMenuSeparator2{&m_calibrateMenu};
|
||||||
|
FMenuItem m_adjustLevel{"&Level Adjustment", &m_calibrateMenu};
|
||||||
|
|
||||||
|
FMenu m_engineeringMenu{"&Engineering", &m_menuBar};
|
||||||
|
FMenuItem m_adjSymLevel{"&Symbol Level Adjustment", &m_engineeringMenu};
|
||||||
|
FMenuItem m_adjHSBandwidth{"Hotspot &Bandwidth Adjustment", &m_engineeringMenu};
|
||||||
|
FMenuItem m_adjHSGain{"Hotspot &Gain & AFC", &m_engineeringMenu};
|
||||||
|
FMenuItem m_engineeringMenuSeparator1{&m_engineeringMenu};
|
||||||
|
FMenuItem m_adjFifoBuffers{"&FIFO Buffers", &m_engineeringMenu};
|
||||||
|
FMenuItem m_engineeringMenuSeparator3{&m_engineeringMenu};
|
||||||
|
FMenuItem m_eraseConfigArea{"Erase Modem Configuration Area", &m_engineeringMenu};
|
||||||
|
FMenuItem m_readConfigArea{"Read Modem Configuration Area", &m_engineeringMenu};
|
||||||
|
FMenuItem m_engineeringMenuSeparator2{&m_engineeringMenu};
|
||||||
|
FCheckMenuItem m_forceHotspot{"Force Hotspot Settings", &m_engineeringMenu};
|
||||||
|
FCheckMenuItem m_modemDebug{"Modem Debug", &m_engineeringMenu};
|
||||||
|
|
||||||
|
FMenu m_helpMenu{"&Help", &m_menuBar};
|
||||||
|
FMenuItem m_aboutItem{"&About", &m_helpMenu};
|
||||||
|
|
||||||
|
FStatusBar m_statusBar{this};
|
||||||
|
FStatusKey m_keyF2{FKey::F2, "Save Settings", &m_statusBar};
|
||||||
|
FStatusKey m_keyF3{FKey::F3, "Quit", &m_statusBar};
|
||||||
|
FStatusKey m_keyF5{FKey::F5, "Level Adjustment", &m_statusBar};
|
||||||
|
FStatusKey m_keyF8{FKey::F8, "Connect to Modem", &m_statusBar};
|
||||||
|
FStatusKey m_keyF12{FKey::F12, "Transmit", &m_statusBar};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to reset the BER window to a default state.
|
||||||
|
/// </summary>
|
||||||
|
void resetBERWnd(bool show = false)
|
||||||
|
{
|
||||||
|
if (show) {
|
||||||
|
m_berWnd.show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_berWnd.hide();
|
||||||
|
}
|
||||||
|
m_berWnd.ber("-.---");
|
||||||
|
m_berWnd.segmentColor(FColor::LightGray);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Event Handlers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
void onClose(FCloseEvent* e) override
|
||||||
|
{
|
||||||
|
// if we are saving on close -- fire off the file save event
|
||||||
|
if (m_saveOnCloseToggle.isChecked()) {
|
||||||
|
m_setup->saveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
if (m_setup->m_transmit)
|
||||||
|
m_setup->setTransmit();
|
||||||
|
|
||||||
|
m_setup->m_isConnected = false;
|
||||||
|
m_setup->m_modem->close();
|
||||||
|
Thread::sleep(250);
|
||||||
|
}
|
||||||
|
|
||||||
|
FApplication::closeConfirmationDialog(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Callbacks
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Save Settings" menu item click callback.
|
||||||
|
/// </summary>
|
||||||
|
void cb_connectToModemClick()
|
||||||
|
{
|
||||||
|
if (!m_setup->m_isConnected) {
|
||||||
|
FMessageBox wait("Wait", L"Please wait...\nConnecting to modem...",
|
||||||
|
FMessageBox::ButtonType::Reject, FMessageBox::ButtonType::Reject, FMessageBox::ButtonType::Reject, this);
|
||||||
|
wait.setCenterText();
|
||||||
|
wait.setModal(false);
|
||||||
|
wait.show();
|
||||||
|
|
||||||
|
// open modem and initialize
|
||||||
|
bool ret = m_setup->m_modem->open();
|
||||||
|
wait.hide();
|
||||||
|
if (!ret) {
|
||||||
|
FMessageBox::error(this, L"Failed to connect to modem!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMessageBox initWait("Wait", L"Please wait...\nInitializing modem...",
|
||||||
|
FMessageBox::ButtonType::Reject, FMessageBox::ButtonType::Reject, FMessageBox::ButtonType::Reject, this);
|
||||||
|
initWait.setCenterText();
|
||||||
|
initWait.setModal(false);
|
||||||
|
initWait.show();
|
||||||
|
|
||||||
|
m_setup->readFlash();
|
||||||
|
|
||||||
|
m_setup->writeFifoLength();
|
||||||
|
m_setup->writeConfig();
|
||||||
|
m_setup->writeRFParams();
|
||||||
|
|
||||||
|
m_setup->getStatus();
|
||||||
|
uint8_t timeout = 0U;
|
||||||
|
while (!m_setup->m_hasFetchedStatus) {
|
||||||
|
m_setup->m_modem->clock(0U);
|
||||||
|
|
||||||
|
timeout++;
|
||||||
|
if (timeout >= 75U) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(5U);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_setup->m_hasFetchedStatus) {
|
||||||
|
FMessageBox::error(this, L"Failed to get status from the modem!");
|
||||||
|
|
||||||
|
m_setup->m_isConnected = false;
|
||||||
|
m_connectToModemItem.setEnable();
|
||||||
|
|
||||||
|
m_setup->m_modem->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_setup->m_isConnected = true;
|
||||||
|
m_connectToModemItem.setDisable();
|
||||||
|
|
||||||
|
m_statusWnd.show();
|
||||||
|
|
||||||
|
m_setup->m_modem->m_statusTimer.start();
|
||||||
|
m_setup->m_stopWatch.start();
|
||||||
|
|
||||||
|
setMenuStates();
|
||||||
|
m_setup->printStatus();
|
||||||
|
|
||||||
|
// set default state
|
||||||
|
m_setup->m_mode = STATE_DMR_CAL;
|
||||||
|
m_setup->m_modeStr = DMR_CAL_STR;
|
||||||
|
m_setup->m_duplex = true;
|
||||||
|
m_setup->m_dmrEnabled = false;
|
||||||
|
m_setup->m_dmrRx1K = false;
|
||||||
|
m_setup->m_p25Enabled = false;
|
||||||
|
m_setup->m_p25Rx1K = false;
|
||||||
|
m_setup->m_p25TduTest = false;
|
||||||
|
m_setup->m_nxdnEnabled = false;
|
||||||
|
m_setup->writeConfig();
|
||||||
|
|
||||||
|
initWait.hide();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FMessageBox::error(this, L"Cannot connect to a modem when already connected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SETUP_WND_H__
|
||||||
@ -0,0 +1,266 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__SITE_PARAM_SET_WND_H__)
|
||||||
|
#define __SITE_PARAM_SET_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include "host/setup/CloseWndBase.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the site parameters configuration window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API SiteParamSetWnd final : public CloseWndBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the SiteParamSetWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit SiteParamSetWnd(HostSetup* setup, FWidget* widget = nullptr) : CloseWndBase{setup, widget}
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FLabel m_cwParams{"CW Configuration", this};
|
||||||
|
FLabel m_siteParams{"Parameters", this};
|
||||||
|
|
||||||
|
FCheckBox m_cwEnabled{"Enabled", this};
|
||||||
|
FLabel m_cwCallsignLabel{"Callsign: ", this};
|
||||||
|
FLineEdit m_cwCallsign{this};
|
||||||
|
FLabel m_cwTimeLabel{"CW Interval: ", this};
|
||||||
|
FSpinBox m_cwTime{this};
|
||||||
|
|
||||||
|
#if defined(ENABLE_DMR)
|
||||||
|
FLabel m_dmrColorCodeLabel{"DMR CC: ", this};
|
||||||
|
FSpinBox m_dmrColorCode{this};
|
||||||
|
#endif // defined(ENABLE_DMR)
|
||||||
|
#if defined(ENABLE_P25)
|
||||||
|
FLabel m_p25NACLabel{"P25 NAC: ", this};
|
||||||
|
FLineEdit m_p25NAC{this};
|
||||||
|
#endif // defined(ENABLE_P25)
|
||||||
|
#if defined(ENABLE_NXDN)
|
||||||
|
FLabel m_nxdnRANLabel{"NXDN RAN: ", this};
|
||||||
|
FSpinBox m_nxdnRAN{this};
|
||||||
|
#endif // defined(ENABLE_NXDN)
|
||||||
|
|
||||||
|
FLabel m_siteIdLabel{"Site ID: ", this};
|
||||||
|
FLineEdit m_siteId{this};
|
||||||
|
#if defined(ENABLE_DMR)
|
||||||
|
FLabel m_dmrNetIdLabel{"DMR Net. ID: ", this};
|
||||||
|
FLineEdit m_dmrNetId{this};
|
||||||
|
#endif // defined(ENABLE_DMR)
|
||||||
|
#if defined(ENABLE_P25)
|
||||||
|
FLabel m_p25NetIdLabel{"P25 Net. ID: ", this};
|
||||||
|
FLineEdit m_p25NetId{this};
|
||||||
|
FLabel m_p25SysIdLabel{"P25 System ID: ", this};
|
||||||
|
FLineEdit m_p25SysId{this};
|
||||||
|
FLabel m_p25RfssIdLabel{"P25 RFSS ID: ", this};
|
||||||
|
FLineEdit m_p25RfssId{this};
|
||||||
|
#endif // defined(ENABLE_P25)
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setText("Site Parameters");
|
||||||
|
FDialog::setSize(FSize{63, 20});
|
||||||
|
|
||||||
|
m_enableSetButton = false;
|
||||||
|
CloseWndBase::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initControls()
|
||||||
|
{
|
||||||
|
yaml::Node cwId = m_setup->m_conf["system"]["cwId"];
|
||||||
|
bool enabled = cwId["enable"].as<bool>(false);
|
||||||
|
uint32_t cwTime = cwId["time"].as<uint32_t>(10U);
|
||||||
|
std::string callsign = cwId["callsign"].as<std::string>();
|
||||||
|
|
||||||
|
// cw configuration
|
||||||
|
{
|
||||||
|
m_cwParams.setGeometry(FPoint(2, 1), FSize(30, 2));
|
||||||
|
m_cwParams.setEmphasis();
|
||||||
|
m_cwParams.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_cwEnabled.setGeometry(FPoint(2, 3), FSize(10, 1));
|
||||||
|
m_cwEnabled.setChecked(enabled);
|
||||||
|
m_cwEnabled.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_conf["system"]["cwId"]["enable"] = __BOOL_STR(m_cwEnabled.isChecked());
|
||||||
|
});
|
||||||
|
|
||||||
|
m_cwCallsignLabel.setGeometry(FPoint(2, 4), FSize(20, 1));
|
||||||
|
m_cwCallsign.setGeometry(FPoint(23, 4), FSize(28, 1));
|
||||||
|
m_cwCallsign.setText(callsign);
|
||||||
|
m_cwCallsign.setShadow(false);
|
||||||
|
m_cwCallsign.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["cwId"]["callsign"] = m_cwCallsign.getText().toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_cwTimeLabel.setGeometry(FPoint(2, 5), FSize(20, 1));
|
||||||
|
m_cwTime.setGeometry(FPoint(23, 5), FSize(10, 1));
|
||||||
|
m_cwTime.setValue(cwTime);
|
||||||
|
m_cwTime.setMinValue(0);
|
||||||
|
m_cwTime.setShadow(false);
|
||||||
|
m_cwTime.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["cwId"]["time"] = __INT_STR(m_cwTime.getValue());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
yaml::Node rfssConfig = m_setup->m_conf["system"]["config"];
|
||||||
|
|
||||||
|
// site parameters
|
||||||
|
{
|
||||||
|
m_siteParams.setGeometry(FPoint(2, 7), FSize(30, 2));
|
||||||
|
m_siteParams.setEmphasis();
|
||||||
|
m_siteParams.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
#if defined(ENABLE_DMR)
|
||||||
|
uint32_t dmrColorCode = rfssConfig["colorCode"].as<uint32_t>(2U);
|
||||||
|
m_dmrColorCodeLabel.setGeometry(FPoint(2, 9), FSize(8, 1));
|
||||||
|
m_dmrColorCode.setGeometry(FPoint(12, 9), FSize(8, 1));
|
||||||
|
m_dmrColorCode.setValue(dmrColorCode);
|
||||||
|
m_dmrColorCode.setRange(0, 15);
|
||||||
|
m_dmrColorCode.setShadow(false);
|
||||||
|
m_dmrColorCode.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["config"]["colorCode"] = __INT_STR(m_dmrColorCode.getValue());
|
||||||
|
});
|
||||||
|
#endif // defined(ENABLE_DMR)
|
||||||
|
|
||||||
|
#if defined(ENABLE_P25)
|
||||||
|
m_p25NACLabel.setGeometry(FPoint(23, 9), FSize(10, 1));
|
||||||
|
m_p25NAC.setGeometry(FPoint(33, 9), FSize(8, 1));
|
||||||
|
m_p25NAC.setText(m_setup->m_conf["system"]["config"]["nac"].as<std::string>("1").c_str());
|
||||||
|
m_p25NAC.setShadow(false);
|
||||||
|
m_p25NAC.setMaxLength(3);
|
||||||
|
m_p25NAC.setInputFilter("[[:xdigit:]]");
|
||||||
|
m_p25NAC.addCallback("changed", [&]() {
|
||||||
|
uint32_t nac = (uint32_t)::strtoul(std::string(m_p25NAC.getText().toString()).c_str(), NULL, 16);
|
||||||
|
nac = p25::P25Utils::nac(nac);
|
||||||
|
|
||||||
|
m_setup->m_conf["system"]["config"]["nac"] = __INT_HEX_STR(nac);
|
||||||
|
});
|
||||||
|
#endif // defined(ENABLE_P25)
|
||||||
|
|
||||||
|
#if defined(ENABLE_NXDN)
|
||||||
|
uint32_t nxdnRAN = rfssConfig["ran"].as<uint32_t>(1U);
|
||||||
|
m_nxdnRANLabel.setGeometry(FPoint(42, 9), FSize(10, 1));
|
||||||
|
m_nxdnRAN.setGeometry(FPoint(53, 9), FSize(8, 1));
|
||||||
|
m_nxdnRAN.setValue(nxdnRAN);
|
||||||
|
m_nxdnRAN.setRange(0, 15);
|
||||||
|
m_nxdnRAN.setShadow(false);
|
||||||
|
m_nxdnRAN.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["config"]["ran"] = __INT_STR(m_nxdnRAN.getValue());
|
||||||
|
});
|
||||||
|
#endif // defined(ENABLE_NXDN)
|
||||||
|
|
||||||
|
m_siteIdLabel.setGeometry(FPoint(2, 10), FSize(20, 1));
|
||||||
|
m_siteId.setGeometry(FPoint(23, 10), FSize(10, 1));
|
||||||
|
m_siteId.setText(rfssConfig["siteId"].as<std::string>("1").c_str());
|
||||||
|
m_siteId.setShadow(false);
|
||||||
|
m_siteId.setMaxLength(3);
|
||||||
|
m_siteId.setInputFilter("[[:xdigit:]]");
|
||||||
|
m_siteId.addCallback("changed", [&]() {
|
||||||
|
uint32_t id = (uint32_t)::strtoul(std::string(m_siteId.getText().toString()).c_str(), NULL, 16);
|
||||||
|
id = p25::P25Utils::siteId(id);
|
||||||
|
|
||||||
|
m_setup->m_conf["system"]["config"]["siteId"] = __INT_HEX_STR(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
#if defined(ENABLE_DMR)
|
||||||
|
m_dmrNetIdLabel.setGeometry(FPoint(2, 11), FSize(20, 1));
|
||||||
|
m_dmrNetId.setGeometry(FPoint(23, 11), FSize(10, 1));
|
||||||
|
m_dmrNetId.setText(rfssConfig["dmrNetId"].as<std::string>("1").c_str());
|
||||||
|
m_dmrNetId.setShadow(false);
|
||||||
|
m_dmrNetId.setMaxLength(6);
|
||||||
|
m_dmrNetId.setInputFilter("[[:xdigit:]]");
|
||||||
|
m_dmrNetId.addCallback("changed", [&]() {
|
||||||
|
uint32_t id = (uint32_t)::strtoul(std::string(m_dmrNetId.getText().toString()).c_str(), NULL, 16);
|
||||||
|
id = dmr::DMRUtils::netId(id, dmr::SITE_MODEL_TINY);
|
||||||
|
|
||||||
|
m_setup->m_conf["system"]["config"]["dmrNetId"] = __INT_HEX_STR(id);
|
||||||
|
});
|
||||||
|
#endif // defined(ENABLE_DMR)
|
||||||
|
|
||||||
|
#if defined(ENABLE_P25)
|
||||||
|
m_p25NetIdLabel.setGeometry(FPoint(2, 12), FSize(20, 1));
|
||||||
|
m_p25NetId.setGeometry(FPoint(23, 12), FSize(10, 1));
|
||||||
|
m_p25NetId.setText(rfssConfig["netId"].as<std::string>("1").c_str());
|
||||||
|
m_p25NetId.setShadow(false);
|
||||||
|
m_p25NetId.setMaxLength(6);
|
||||||
|
m_p25NetId.setInputFilter("[[:xdigit:]]");
|
||||||
|
m_p25NetId.addCallback("changed", [&]() {
|
||||||
|
uint32_t id = (uint32_t)::strtoul(std::string(m_p25NetId.getText().toString()).c_str(), NULL, 16);
|
||||||
|
id = p25::P25Utils::netId(id);
|
||||||
|
|
||||||
|
m_setup->m_conf["system"]["config"]["netId"] = __INT_HEX_STR(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_p25SysIdLabel.setGeometry(FPoint(2, 13), FSize(20, 1));
|
||||||
|
m_p25SysId.setGeometry(FPoint(23, 13), FSize(10, 1));
|
||||||
|
m_p25SysId.setText(rfssConfig["sysId"].as<std::string>("1").c_str());
|
||||||
|
m_p25SysId.setShadow(false);
|
||||||
|
m_p25SysId.setMaxLength(4);
|
||||||
|
m_p25SysId.setInputFilter("[[:xdigit:]]");
|
||||||
|
m_p25SysId.addCallback("changed", [&]() {
|
||||||
|
uint32_t id = (uint32_t)::strtoul(std::string(m_p25SysId.getText().toString()).c_str(), NULL, 16);
|
||||||
|
id = p25::P25Utils::sysId(id);
|
||||||
|
|
||||||
|
m_setup->m_conf["system"]["config"]["sysId"] = __INT_HEX_STR(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_p25RfssIdLabel.setGeometry(FPoint(2, 14), FSize(20, 1));
|
||||||
|
m_p25RfssId.setGeometry(FPoint(23, 14), FSize(10, 1));
|
||||||
|
m_p25RfssId.setText(rfssConfig["dmrNetId"].as<std::string>("1").c_str());
|
||||||
|
m_p25RfssId.setShadow(false);
|
||||||
|
m_p25RfssId.setMaxLength(3);
|
||||||
|
m_p25RfssId.setInputFilter("[[:xdigit:]]");
|
||||||
|
m_p25RfssId.addCallback("changed", [&]() {
|
||||||
|
uint32_t id = (uint8_t)::strtoul(std::string(m_p25RfssId.getText().toString()).c_str(), NULL, 16);
|
||||||
|
id = p25::P25Utils::rfssId(id);
|
||||||
|
|
||||||
|
m_setup->m_conf["system"]["config"]["rfssId"] = __INT_HEX_STR(id);
|
||||||
|
});
|
||||||
|
#endif // defined(ENABLE_P25)
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseWndBase::initControls();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SITE_PARAM_SET_WND_H__
|
||||||
@ -0,0 +1,184 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__SYMB_LEVEL_ADJUST_WND_H__)
|
||||||
|
#define __SYMB_LEVEL_ADJUST_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include "host/setup/AdjustWndBase.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the symbol level adjustment window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API SymbLevelAdjustWnd final : public AdjustWndBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the SymbLevelAdjustWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit SymbLevelAdjustWnd(HostSetup* setup, FWidget* widget = nullptr) : AdjustWndBase{setup, widget}
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FLabel m_symbLevelLabel{"Symbol Levels", this};
|
||||||
|
FLabel m_dmr3LevelLabel{"DMR +/- 3 Symbol Level: ", this};
|
||||||
|
FLabel m_dmr1LevelLabel{"DMR +/- 1 Symbol Level: ", this};
|
||||||
|
FLabel m_p253LevelLabel{"P25 +/- 3 Symbol Level: ", this};
|
||||||
|
FLabel m_p251LevelLabel{"P25 +/- 1 Symbol Level: ", this};
|
||||||
|
FLabel m_nxdn3LevelLabel{"NXDN +/- 3 Symbol Level: ", this};
|
||||||
|
FLabel m_nxdn1LevelLabel{"NXDN +/- 1 Symbol Level: ", this};
|
||||||
|
|
||||||
|
FSpinBox m_dmr3Level{this};
|
||||||
|
FSpinBox m_dmr1Level{this};
|
||||||
|
FSpinBox m_p253Level{this};
|
||||||
|
FSpinBox m_p251Level{this};
|
||||||
|
FSpinBox m_nxdn3Level{this};
|
||||||
|
FSpinBox m_nxdn1Level{this};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setText("Symbol Level Adjustment");
|
||||||
|
FDialog::setSize(FSize{60, 16});
|
||||||
|
|
||||||
|
AdjustWndBase::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initControls()
|
||||||
|
{
|
||||||
|
// symbol levels
|
||||||
|
{
|
||||||
|
m_symbLevelLabel.setGeometry(FPoint(2, 1), FSize(20, 2));
|
||||||
|
m_symbLevelLabel.setEmphasis();
|
||||||
|
m_symbLevelLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_dmr3LevelLabel.setGeometry(FPoint(2, 3), FSize(25, 1));
|
||||||
|
m_dmr3Level.setGeometry(FPoint(28, 3), FSize(10, 1));
|
||||||
|
m_dmr3Level.setRange(-127, 127);
|
||||||
|
m_dmr3Level.setValue(m_setup->m_modem->m_dmrSymLevel3Adj);
|
||||||
|
m_dmr3Level.setShadow(false);
|
||||||
|
m_dmr3Level.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_dmrSymLevel3Adj = m_dmr3Level.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_dmr1LevelLabel.setGeometry(FPoint(2, 4), FSize(25, 1));
|
||||||
|
m_dmr1Level.setGeometry(FPoint(28, 4), FSize(10, 1));
|
||||||
|
m_dmr1Level.setRange(-127, 127);
|
||||||
|
m_dmr1Level.setValue(m_setup->m_modem->m_dmrSymLevel1Adj);
|
||||||
|
m_dmr1Level.setShadow(false);
|
||||||
|
m_dmr1Level.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_dmrSymLevel1Adj = m_dmr1Level.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_p253LevelLabel.setGeometry(FPoint(2, 5), FSize(25, 1));
|
||||||
|
m_p253Level.setGeometry(FPoint(28, 5), FSize(10, 1));
|
||||||
|
m_p253Level.setRange(-127, 127);
|
||||||
|
m_p253Level.setValue(m_setup->m_modem->m_p25SymLevel3Adj);
|
||||||
|
m_p253Level.setShadow(false);
|
||||||
|
m_p253Level.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_p25SymLevel3Adj = m_p253Level.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_p251LevelLabel.setGeometry(FPoint(2, 6), FSize(25, 1));
|
||||||
|
m_p251Level.setGeometry(FPoint(28, 6), FSize(10, 1));
|
||||||
|
m_p251Level.setRange(-127, 127);
|
||||||
|
m_p251Level.setValue(m_setup->m_modem->m_p25SymLevel1Adj);
|
||||||
|
m_p251Level.setShadow(false);
|
||||||
|
m_p251Level.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_p25SymLevel1Adj = m_p251Level.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_nxdn3LevelLabel.setGeometry(FPoint(2, 7), FSize(25, 1));
|
||||||
|
m_nxdn3Level.setGeometry(FPoint(28, 7), FSize(10, 1));
|
||||||
|
m_nxdn3Level.setRange(-127, 127);
|
||||||
|
m_nxdn3Level.setValue(m_setup->m_modem->m_nxdnSymLevel3Adj);
|
||||||
|
m_nxdn3Level.setShadow(false);
|
||||||
|
m_nxdn3Level.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_nxdnSymLevel3Adj = m_nxdn3Level.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_nxdn1LevelLabel.setGeometry(FPoint(2, 8), FSize(25, 1));
|
||||||
|
m_nxdn1Level.setGeometry(FPoint(28, 8), FSize(10, 1));
|
||||||
|
m_nxdn1Level.setRange(-127, 127);
|
||||||
|
m_nxdn1Level.setValue(m_setup->m_modem->m_nxdnSymLevel1Adj);
|
||||||
|
m_nxdn1Level.setShadow(false);
|
||||||
|
m_nxdn1Level.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_modem->m_nxdnSymLevel1Adj = m_nxdn1Level.getValue();
|
||||||
|
Thread::sleep(2);
|
||||||
|
m_setup->writeConfig();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup control states
|
||||||
|
if (m_setup->m_isConnected) {
|
||||||
|
if (m_setup->m_modem->m_isHotspot) {
|
||||||
|
m_dmr3Level.setDisable();
|
||||||
|
m_dmr1Level.setDisable();
|
||||||
|
m_p253Level.setDisable();
|
||||||
|
m_p251Level.setDisable();
|
||||||
|
m_nxdn3Level.setDisable();
|
||||||
|
m_nxdn1Level.setDisable();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_dmr3Level.setEnable();
|
||||||
|
m_dmr1Level.setEnable();
|
||||||
|
m_p253Level.setEnable();
|
||||||
|
m_p251Level.setEnable();
|
||||||
|
m_nxdn3Level.setEnable();
|
||||||
|
m_nxdn1Level.setEnable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AdjustWndBase::initControls();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SYMB_LEVEL_ADJUST_WND_H__
|
||||||
@ -0,0 +1,244 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Host Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
#if !defined(__SYSTEM_CONFIG_SET_WND_H__)
|
||||||
|
#define __SYSTEM_CONFIG_SET_WND_H__
|
||||||
|
|
||||||
|
#include "host/setup/HostSetup.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include "host/setup/CloseWndBase.h"
|
||||||
|
|
||||||
|
#include <final/final.h>
|
||||||
|
using namespace finalcut;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the system configuration window.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API SystemConfigSetWnd final : public CloseWndBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the SystemConfigSetWnd class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setup"></param>
|
||||||
|
/// <param name="widget"></param>
|
||||||
|
explicit SystemConfigSetWnd(HostSetup* setup, FWidget* widget = nullptr) : CloseWndBase{setup, widget}
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FLabel m_portAndSpeedLabel{"Modem Port and Speed", this};
|
||||||
|
FLabel m_systemSettingsLabel{"System Settings", this};
|
||||||
|
FLabel m_modeSettingsLabel{"Mode Settings", this};
|
||||||
|
|
||||||
|
FLabel m_modemPortLabel{"Modem Port: ", this};
|
||||||
|
FLineEdit m_modemPort{this};
|
||||||
|
FLabel m_modemSpeedLabel{"Modem Speed: ", this};
|
||||||
|
FSpinBox m_modemSpeed{this};
|
||||||
|
|
||||||
|
FLabel m_identityLabel{"Identity: ", this};
|
||||||
|
FLineEdit m_identity{this};
|
||||||
|
FCheckBox m_duplex{"Duplex", this};
|
||||||
|
FCheckBox m_simplexFreq{"Simplex Freq", this};
|
||||||
|
FLabel m_timeoutLabel{"Timeout: ", this};
|
||||||
|
FSpinBox m_timeout{this};
|
||||||
|
FLabel m_modeHangLabel{"Mode Hangtime: ", this};
|
||||||
|
FSpinBox m_modeHang{this};
|
||||||
|
FLabel m_rfTalkgroupLabel{"RF TG Hangtime: ", this};
|
||||||
|
FSpinBox m_rfTalkgroup{this};
|
||||||
|
|
||||||
|
FCheckBox m_fixedMode{"Fixed Mode", this};
|
||||||
|
#if defined(ENABLE_DMR)
|
||||||
|
FCheckBox m_dmrEnabled{"DMR", this};
|
||||||
|
#endif // defined(ENABLE_DMR)
|
||||||
|
#if defined(ENABLE_P25)
|
||||||
|
FCheckBox m_p25Enabled{"P25", this};
|
||||||
|
#endif // defined(ENABLE_P25)
|
||||||
|
#if defined(ENABLE_NXDN)
|
||||||
|
FCheckBox m_nxdnEnabled{"NXDN", this};
|
||||||
|
#endif // defined(ENABLE_NXDN)
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initLayout() override
|
||||||
|
{
|
||||||
|
FDialog::setText("System Configuration");
|
||||||
|
FDialog::setSize(FSize{56, 22});
|
||||||
|
|
||||||
|
m_enableSetButton = false;
|
||||||
|
CloseWndBase::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void initControls()
|
||||||
|
{
|
||||||
|
yaml::Node modemConfig = m_setup->m_conf["system"]["modem"];
|
||||||
|
m_setup->m_conf["system"]["modem"]["protocol"]["type"] = std::string("uart"); // configuring modem, always sets type to UART
|
||||||
|
|
||||||
|
yaml::Node uartConfig = modemConfig["protocol"]["uart"];
|
||||||
|
|
||||||
|
std::string modemPort = uartConfig["port"].as<std::string>("/dev/ttyUSB0");
|
||||||
|
uint32_t portSpeed = uartConfig["speed"].as<uint32_t>(115200);
|
||||||
|
|
||||||
|
// port and speed
|
||||||
|
{
|
||||||
|
m_portAndSpeedLabel.setGeometry(FPoint(2, 1), FSize(30, 2));
|
||||||
|
m_portAndSpeedLabel.setEmphasis();
|
||||||
|
m_portAndSpeedLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_modemPortLabel.setGeometry(FPoint(2, 3), FSize(20, 1));
|
||||||
|
m_modemPort.setGeometry(FPoint(23, 3), FSize(28, 1));
|
||||||
|
m_modemPort.setText(modemPort);
|
||||||
|
m_modemPort.setShadow(false);
|
||||||
|
m_modemPort.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["modem"]["protocol"]["uart"]["port"] = m_modemPort.getText().toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_modemSpeedLabel.setGeometry(FPoint(2, 4), FSize(20, 1));
|
||||||
|
m_modemSpeed.setGeometry(FPoint(23, 4), FSize(10, 1));
|
||||||
|
m_modemSpeed.setRange(1200, 460800);
|
||||||
|
m_modemSpeed.setValue(portSpeed);
|
||||||
|
m_modemSpeed.setShadow(false);
|
||||||
|
m_modemSpeed.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["modem"]["protocol"]["uart"]["speed"] = __INT_STR(m_modemSpeed.getValue());
|
||||||
|
});
|
||||||
|
m_modemSpeed.setDisable(); // don't allow this to be changed right now
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string identity = m_setup->m_conf["system"]["identity"].as<std::string>();
|
||||||
|
uint32_t timeout = m_setup->m_conf["system"]["timeout"].as<uint32_t>();
|
||||||
|
bool duplex = m_setup->m_conf["system"]["duplex"].as<bool>(true);
|
||||||
|
bool simplexSameFrequency = m_setup->m_conf["system"]["simplexSameFrequency"].as<bool>(false);
|
||||||
|
uint32_t modeHang = m_setup->m_conf["system"]["modeHang"].as<uint32_t>();
|
||||||
|
uint32_t rfTalkgroupHang = m_setup->m_conf["system"]["rfTalkgroupHang"].as<uint32_t>();
|
||||||
|
bool fixedMode = m_setup->m_conf["system"]["fixedMode"].as<bool>(false);
|
||||||
|
|
||||||
|
// system settings
|
||||||
|
{
|
||||||
|
m_systemSettingsLabel.setGeometry(FPoint(2, 6), FSize(30, 2));
|
||||||
|
m_systemSettingsLabel.setEmphasis();
|
||||||
|
m_systemSettingsLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_identityLabel.setGeometry(FPoint(2, 8), FSize(20, 1));
|
||||||
|
m_identity.setGeometry(FPoint(23, 8), FSize(28, 1));
|
||||||
|
m_identity.setText(identity);
|
||||||
|
m_identity.setShadow(false);
|
||||||
|
m_identity.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["identity"] = m_identity.getText().toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_duplex.setGeometry(FPoint(2, 9), FSize(10, 1));
|
||||||
|
m_duplex.setChecked(duplex);
|
||||||
|
m_duplex.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_conf["system"]["duplex"] = __BOOL_STR(m_duplex.isChecked());
|
||||||
|
});
|
||||||
|
|
||||||
|
m_simplexFreq.setGeometry(FPoint(15, 9), FSize(10, 1));
|
||||||
|
m_simplexFreq.setChecked(simplexSameFrequency);
|
||||||
|
m_simplexFreq.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_conf["system"]["duplex"] = __BOOL_STR(m_simplexFreq.isChecked());
|
||||||
|
});
|
||||||
|
|
||||||
|
m_timeoutLabel.setGeometry(FPoint(2, 10), FSize(20, 1));
|
||||||
|
m_timeout.setGeometry(FPoint(23, 10), FSize(10, 1));
|
||||||
|
m_timeout.setValue(timeout);
|
||||||
|
m_timeout.setMinValue(0);
|
||||||
|
m_timeout.setShadow(false);
|
||||||
|
m_timeout.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["timeout"] = __INT_STR(m_modeHang.getValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
m_modeHangLabel.setGeometry(FPoint(2, 11), FSize(20, 1));
|
||||||
|
m_modeHang.setGeometry(FPoint(23, 11), FSize(10, 1));
|
||||||
|
m_modeHang.setValue(modeHang);
|
||||||
|
m_modeHang.setMinValue(0);
|
||||||
|
m_modeHang.setShadow(false);
|
||||||
|
m_modeHang.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["modeHang"] = __INT_STR(m_modeHang.getValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
m_rfTalkgroupLabel.setGeometry(FPoint(2, 12), FSize(20, 1));
|
||||||
|
m_rfTalkgroup.setGeometry(FPoint(23, 12), FSize(10, 1));
|
||||||
|
m_rfTalkgroup.setValue(rfTalkgroupHang);
|
||||||
|
m_rfTalkgroup.setMinValue(0);
|
||||||
|
m_rfTalkgroup.setShadow(false);
|
||||||
|
m_rfTalkgroup.addCallback("changed", [&]() {
|
||||||
|
m_setup->m_conf["system"]["rfTalkgroupHang"] = __INT_STR(m_rfTalkgroup.getValue());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// mode settings
|
||||||
|
{
|
||||||
|
m_modeSettingsLabel.setGeometry(FPoint(2, 14), FSize(30, 2));
|
||||||
|
m_modeSettingsLabel.setEmphasis();
|
||||||
|
m_modeSettingsLabel.setAlignment(Align::Center);
|
||||||
|
|
||||||
|
m_fixedMode.setGeometry(FPoint(2, 16), FSize(10, 1));
|
||||||
|
m_fixedMode.setChecked(fixedMode);
|
||||||
|
m_fixedMode.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_conf["system"]["fixedMode"] = __BOOL_STR(m_fixedMode.isChecked());
|
||||||
|
});
|
||||||
|
|
||||||
|
#if defined(ENABLE_DMR)
|
||||||
|
bool dmrEnabled = m_setup->m_conf["protocols"]["dmr"]["enable"].as<bool>(true);
|
||||||
|
|
||||||
|
m_dmrEnabled.setGeometry(FPoint(2, 17), FSize(10, 1));
|
||||||
|
m_dmrEnabled.setChecked(dmrEnabled);
|
||||||
|
m_dmrEnabled.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_conf["protocols"]["dmr"]["enable"] = __BOOL_STR(m_dmrEnabled.isChecked());
|
||||||
|
});
|
||||||
|
#endif // defined(ENABLE_DMR)
|
||||||
|
#if defined(ENABLE_P25)
|
||||||
|
bool p25Enabled = m_setup->m_conf["protocols"]["p25"]["enable"].as<bool>(true);
|
||||||
|
|
||||||
|
m_p25Enabled.setGeometry(FPoint(12, 17), FSize(10, 1));
|
||||||
|
m_p25Enabled.setChecked(p25Enabled);
|
||||||
|
m_p25Enabled.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_conf["protocols"]["p25"]["enable"] = __BOOL_STR(m_p25Enabled.isChecked());
|
||||||
|
});
|
||||||
|
#endif // defined(ENABLE_P25)
|
||||||
|
#if defined(ENABLE_NXDN)
|
||||||
|
bool nxdnEnabled = m_setup->m_conf["protocols"]["nxdn"]["enable"].as<bool>(true);
|
||||||
|
|
||||||
|
m_nxdnEnabled.setGeometry(FPoint(22, 17), FSize(10, 1));
|
||||||
|
m_nxdnEnabled.setChecked(nxdnEnabled);
|
||||||
|
m_nxdnEnabled.addCallback("toggled", [&]() {
|
||||||
|
m_setup->m_conf["protocols"]["nxdn"]["enable"] = __BOOL_STR(m_nxdnEnabled.isChecked());
|
||||||
|
});
|
||||||
|
#endif // defined(ENABLE_NXDN)
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseWndBase::initControls();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __SYSTEM_CONFIG_SET_WND_H__
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue