diff --git a/DVMConsole.sln b/DVMConsole.sln
index 38bd846..ad4ef8c 100644
--- a/DVMConsole.sln
+++ b/DVMConsole.sln
@@ -3,14 +3,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34330.188
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DVMConsole", "DVMConsole\DVMConsole.csproj", "{710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dvmconsole", "DVMConsole\dvmconsole.csproj", "{710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B5A7CF60-CCDE-4B2B-85C1-86AE3A19FB31}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fnecore", "fnecore\fnecore.csproj", "{1F06ECB1-9928-1430-63F4-2E01522A0510}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fnecore", "fnecore\fnecore.csproj", "{1F06ECB1-9928-1430-63F4-2E01522A0510}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/DVMConsole/AliasTools.cs b/DVMConsole/AliasTools.cs
index 6428c8f..064a0bd 100644
--- a/DVMConsole/AliasTools.cs
+++ b/DVMConsole/AliasTools.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Caleb, K4PHP
@@ -12,23 +12,46 @@
*/
using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using YamlDotNet.Serialization.NamingConventions;
+
using YamlDotNet.Serialization;
-using System.Diagnostics;
+using YamlDotNet.Serialization.NamingConventions;
-namespace DVMConsole
+namespace dvmconsole
{
+ ///
+ ///
+ ///
+ public class RadioAlias
+ {
+ ///
+ ///
+ ///
+ public string Alias { get; set; }
+ ///
+ ///
+ ///
+ public int Rid { get; set; }
+ } //public class RadioAlias
+
+ ///
+ ///
+ ///
public static class AliasTools
{
+ /*
+ ** Methods
+ */
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public static List LoadAliases(string filePath)
{
if (!File.Exists(filePath))
- {
throw new FileNotFoundException("Alias file not found.", filePath);
- }
var deserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
@@ -38,6 +61,12 @@ namespace DVMConsole
return deserializer.Deserialize>(yamlText);
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public static string GetAliasByRid(List aliases, int rid)
{
if (aliases == null || aliases.Count == 0)
@@ -46,11 +75,5 @@ namespace DVMConsole
var match = aliases.FirstOrDefault(a => a.Rid == rid);
return match?.Alias ?? string.Empty;
}
- }
-
- public class RadioAlias
- {
- public string Alias { get; set; }
- public int Rid { get; set; }
- }
-}
+ } //public static class AliasTools
+} // namespace DVMConsole
diff --git a/DVMConsole/AmbeNative.cs b/DVMConsole/AmbeNative.cs
index b9575a6..a983ebd 100644
--- a/DVMConsole/AmbeNative.cs
+++ b/DVMConsole/AmbeNative.cs
@@ -11,8 +11,10 @@
*
*/
+using System.Runtime.InteropServices;
+
#if WIN32
-namespace DVMConsole
+namespace dvmconsole
{
///
/// Implements P/Invoke to callback into external AMBE encoder/decoder library.
@@ -447,4 +449,4 @@ namespace DVMConsole
private static extern uint ambe_voice_enc([Out] IntPtr codeword, [In] short bitSteal, [In] IntPtr samples, [In] short sampleLength, [In] ushort cmode, [In] short n, [In] short unk, [In] IntPtr state);
} // public class AmbeVocoder
}
-#endif
\ No newline at end of file
+#endif
diff --git a/DVMConsole/App.xaml b/DVMConsole/App.xaml
index 6263b34..c1064e9 100644
--- a/DVMConsole/App.xaml
+++ b/DVMConsole/App.xaml
@@ -1,4 +1,4 @@
-
diff --git a/DVMConsole/App.xaml.cs b/DVMConsole/App.xaml.cs
index 9facf73..a9a5e37 100644
--- a/DVMConsole/App.xaml.cs
+++ b/DVMConsole/App.xaml.cs
@@ -1,14 +1,25 @@
-using System.Configuration;
-using System.Data;
+// SPDX-License-Identifier: AGPL-3.0-only
+/**
+* Digital Voice Modem - Desktop Dispatch Console
+* AGPLv3 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Desktop Dispatch Console
+* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
+*
+* Copyright (C) 2025 Caleb, K4PHP
+*
+*/
+
using System.Windows;
-namespace DVMConsole
+namespace dvmconsole
{
///
- /// Interaction logic for App.xaml
+ ///
///
public partial class App : Application
{
+ /* stub */
}
-
-}
+} // namespace dvmconsole
diff --git a/DVMConsole/AssemblyInfo.cs b/DVMConsole/AssemblyInfo.cs
index b0ec827..e2f2862 100644
--- a/DVMConsole/AssemblyInfo.cs
+++ b/DVMConsole/AssemblyInfo.cs
@@ -1,3 +1,16 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+/**
+* Digital Voice Modem - Desktop Dispatch Console
+* AGPLv3 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Desktop Dispatch Console
+* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
+*
+* Copyright (C) 2025 Caleb, K4PHP
+*
+*/
+
using System.Windows;
[assembly: ThemeInfo(
diff --git a/DVMConsole/Assets/WhackerLinkLogoV4.png b/DVMConsole/Assets/WhackerLinkLogoV4.png
deleted file mode 100644
index 683592a..0000000
Binary files a/DVMConsole/Assets/WhackerLinkLogoV4.png and /dev/null differ
diff --git a/DVMConsole/AudioConverter.cs b/DVMConsole/AudioConverter.cs
index 021fc31..c4db6f5 100644
--- a/DVMConsole/AudioConverter.cs
+++ b/DVMConsole/AudioConverter.cs
@@ -1,17 +1,17 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Caleb, K4PHP
*
*/
-namespace DVMConsole
+namespace dvmconsole
{
///
/// Helper to convert audio between different chunk sizes
diff --git a/DVMConsole/AudioManager.cs b/DVMConsole/AudioManager.cs
index c925342..eb442a7 100644
--- a/DVMConsole/AudioManager.cs
+++ b/DVMConsole/AudioManager.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Caleb, K4PHP
@@ -14,23 +14,27 @@
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
-namespace DVMConsole
+namespace dvmconsole
{
///
- /// Class for managing audio streams
+ /// Class for managing audio streams.
///
public class AudioManager
{
- private Dictionary _talkgroupProviders;
- private SettingsManager _settingsManager;
+ private Dictionary talkgroupProviders;
+ private SettingsManager settingsManager;
+
+ /*
+ ** Methods
+ */
///
- /// Creates an instance of
+ /// Creates an instance of class.
///
public AudioManager(SettingsManager settingsManager)
{
- _settingsManager = settingsManager;
- _talkgroupProviders = new Dictionary();
+ this.settingsManager = settingsManager;
+ talkgroupProviders = new Dictionary();
}
///
@@ -40,10 +44,10 @@ namespace DVMConsole
///
public void AddTalkgroupStream(string talkgroupId, byte[] audioData)
{
- if (!_talkgroupProviders.ContainsKey(talkgroupId))
+ if (!talkgroupProviders.ContainsKey(talkgroupId))
AddTalkgroupStream(talkgroupId);
- _talkgroupProviders[talkgroupId].buffer.AddSamples(audioData, 0, audioData.Length);
+ talkgroupProviders[talkgroupId].buffer.AddSamples(audioData, 0, audioData.Length);
}
///
@@ -52,34 +56,19 @@ namespace DVMConsole
///
private void AddTalkgroupStream(string talkgroupId)
{
- int deviceIndex = _settingsManager.ChannelOutputDevices.ContainsKey(talkgroupId) ? _settingsManager.ChannelOutputDevices[talkgroupId] : 0;
-
- var waveOut = new WaveOutEvent
- {
- DeviceNumber = deviceIndex
- };
-
- var bufferProvider = new BufferedWaveProvider(new WaveFormat(8000, 16, 1))
- {
- DiscardOnBufferOverflow = true
- };
+ int deviceIndex = settingsManager.ChannelOutputDevices.ContainsKey(talkgroupId) ? settingsManager.ChannelOutputDevices[talkgroupId] : 0;
- var gainProvider = new GainSampleProvider(bufferProvider.ToSampleProvider())
- {
- Gain = 1.0f
- };
-
- var mixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(8000, 1))
- {
- ReadFully = true
- };
+ var waveOut = new WaveOutEvent { DeviceNumber = deviceIndex };
+ var bufferProvider = new BufferedWaveProvider(new WaveFormat(8000, 16, 1)) { DiscardOnBufferOverflow = true };
+ var gainProvider = new GainSampleProvider(bufferProvider.ToSampleProvider()) { Gain = 1.0f };
+ var mixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(8000, 1)) { ReadFully = true };
mixer.AddMixerInput(gainProvider);
waveOut.Init(mixer);
waveOut.Play();
- _talkgroupProviders[talkgroupId] = (waveOut, mixer, bufferProvider, gainProvider);
+ talkgroupProviders[talkgroupId] = (waveOut, mixer, bufferProvider, gainProvider);
}
///
@@ -87,14 +76,12 @@ namespace DVMConsole
///
public void SetTalkgroupVolume(string talkgroupId, float volume)
{
- if (_talkgroupProviders.ContainsKey(talkgroupId))
- {
- _talkgroupProviders[talkgroupId].gainProvider.Gain = volume;
- }
+ if (talkgroupProviders.ContainsKey(talkgroupId))
+ talkgroupProviders[talkgroupId].gainProvider.Gain = volume;
else
{
AddTalkgroupStream(talkgroupId);
- _talkgroupProviders[talkgroupId].gainProvider.Gain = volume;
+ talkgroupProviders[talkgroupId].gainProvider.Gain = volume;
}
}
@@ -105,13 +92,13 @@ namespace DVMConsole
///
public void SetTalkgroupOutputDevice(string talkgroupId, int deviceIndex)
{
- if (_talkgroupProviders.ContainsKey(talkgroupId))
+ if (talkgroupProviders.ContainsKey(talkgroupId))
{
- _talkgroupProviders[talkgroupId].waveOut.Stop();
- _talkgroupProviders.Remove(talkgroupId);
+ talkgroupProviders[talkgroupId].waveOut.Stop();
+ talkgroupProviders.Remove(talkgroupId);
}
- _settingsManager.UpdateChannelOutputDevice(talkgroupId, deviceIndex);
+ settingsManager.UpdateChannelOutputDevice(talkgroupId, deviceIndex);
AddTalkgroupStream(talkgroupId);
}
@@ -120,8 +107,8 @@ namespace DVMConsole
///
public void Stop()
{
- foreach (var provider in _talkgroupProviders.Values)
+ foreach (var provider in talkgroupProviders.Values)
provider.waveOut.Stop();
}
- }
-}
+ } // public class AudioManager
+} // namespace dvmconsole
diff --git a/DVMConsole/AudioSettingsWindow.xaml b/DVMConsole/AudioSettingsWindow.xaml
index 6f5c59e..cdff37c 100644
--- a/DVMConsole/AudioSettingsWindow.xaml
+++ b/DVMConsole/AudioSettingsWindow.xaml
@@ -1,4 +1,4 @@
-
+ /// Interaction logic for AudioSettingsWindow.xaml.
+ ///
public partial class AudioSettingsWindow : Window
{
- private readonly SettingsManager _settingsManager;
- private readonly AudioManager _audioManager;
- private readonly List _channels;
- private readonly Dictionary _selectedOutputDevices = new Dictionary();
-
+ private readonly SettingsManager settingsManager;
+ private readonly AudioManager audioManager;
+ private readonly List channels;
+ private readonly Dictionary selectedOutputDevices = new Dictionary();
+
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ ///
public AudioSettingsWindow(SettingsManager settingsManager, AudioManager audioManager, List channels)
{
InitializeComponent();
- _settingsManager = settingsManager;
- _audioManager = audioManager;
- _channels = channels;
+ this.settingsManager = settingsManager;
+ this.audioManager = audioManager;
+ this.channels = channels;
LoadAudioDevices();
LoadChannelOutputSettings();
}
+ ///
+ ///
+ ///
private void LoadAudioDevices()
{
List inputDevices = GetAudioInputDevices();
List outputDevices = GetAudioOutputDevices();
InputDeviceComboBox.ItemsSource = inputDevices;
- InputDeviceComboBox.SelectedIndex = _settingsManager.ChannelOutputDevices.ContainsKey("GLOBAL_INPUT")
- ? _settingsManager.ChannelOutputDevices["GLOBAL_INPUT"]
- : 0;
+ InputDeviceComboBox.SelectedIndex = settingsManager.ChannelOutputDevices.ContainsKey("GLOBAL_INPUT")
+ ? settingsManager.ChannelOutputDevices["GLOBAL_INPUT"] : 0;
}
+ ///
+ ///
+ ///
private void LoadChannelOutputSettings()
{
List outputDevices = GetAudioOutputDevices();
- foreach (var channel in _channels)
+ foreach (var channel in channels)
{
TextBlock channelLabel = new TextBlock
{
@@ -65,15 +82,15 @@ namespace DVMConsole
{
Width = 350,
ItemsSource = outputDevices,
- SelectedIndex = _settingsManager.ChannelOutputDevices.ContainsKey(channel.Tgid)
- ? _settingsManager.ChannelOutputDevices[channel.Tgid]
+ SelectedIndex = settingsManager.ChannelOutputDevices.ContainsKey(channel.Tgid)
+ ? settingsManager.ChannelOutputDevices[channel.Tgid]
: 0
};
outputDeviceComboBox.SelectionChanged += (s, e) =>
{
int selectedIndex = outputDeviceComboBox.SelectedIndex;
- _selectedOutputDevices[channel.Tgid] = selectedIndex;
+ selectedOutputDevices[channel.Tgid] = selectedIndex;
};
ChannelOutputStackPanel.Children.Add(channelLabel);
@@ -81,6 +98,10 @@ namespace DVMConsole
}
}
+ ///
+ ///
+ ///
+ ///
private List GetAudioInputDevices()
{
List inputDevices = new List();
@@ -94,6 +115,10 @@ namespace DVMConsole
return inputDevices;
}
+ ///
+ ///
+ ///
+ ///
private List GetAudioOutputDevices()
{
List outputDevices = new List();
@@ -107,25 +132,35 @@ namespace DVMConsole
return outputDevices;
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
int selectedInputIndex = InputDeviceComboBox.SelectedIndex;
- _settingsManager.UpdateChannelOutputDevice("GLOBAL_INPUT", selectedInputIndex);
+ settingsManager.UpdateChannelOutputDevice("GLOBAL_INPUT", selectedInputIndex);
- foreach (var entry in _selectedOutputDevices)
+ foreach (var entry in selectedOutputDevices)
{
- _settingsManager.UpdateChannelOutputDevice(entry.Key, entry.Value);
- _audioManager.SetTalkgroupOutputDevice(entry.Key, entry.Value);
+ settingsManager.UpdateChannelOutputDevice(entry.Key, entry.Value);
+ audioManager.SetTalkgroupOutputDevice(entry.Key, entry.Value);
}
DialogResult = true;
Close();
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
- }
-}
+ } // public partial class AudioSettingsWindow : Window
+} // namespace dvmconsole
diff --git a/DVMConsole/CallHistoryWindow.xaml b/DVMConsole/CallHistoryWindow.xaml
index e593803..a58a974 100644
--- a/DVMConsole/CallHistoryWindow.xaml
+++ b/DVMConsole/CallHistoryWindow.xaml
@@ -1,9 +1,9 @@
-
diff --git a/DVMConsole/CallHistoryWindow.xaml.cs b/DVMConsole/CallHistoryWindow.xaml.cs
index e8566a6..8cb361b 100644
--- a/DVMConsole/CallHistoryWindow.xaml.cs
+++ b/DVMConsole/CallHistoryWindow.xaml.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Caleb, K4PHP
@@ -15,12 +15,91 @@ using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Media;
-namespace DVMConsole
+namespace dvmconsole
{
+ ///
+ ///
+ ///
+ public class CallHistoryViewModel
+ {
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
+ public ObservableCollection CallHistory { get; set; }
+
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CallHistoryViewModel()
+ {
+ CallHistory = new ObservableCollection();
+ }
+ } // public class CallHistoryViewModel
+
+ ///
+ ///
+ ///
+ public class CallEntry : DependencyObject
+ {
+ public static readonly DependencyProperty BackgroundColorProperty =
+ DependencyProperty.Register(nameof(BackgroundColor), typeof(Brush), typeof(CallEntry), new PropertyMetadata(Brushes.Transparent));
+
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
+ public string Channel { get; set; }
+ ///
+ ///
+ ///
+ public int SrcId { get; set; }
+ ///
+ ///
+ ///
+ public int DstId { get; set; }
+
+ ///
+ ///
+ ///
+ public Brush BackgroundColor
+ {
+ get { return (Brush)GetValue(BackgroundColorProperty); }
+ set { SetValue(BackgroundColorProperty, value); }
+ }
+ } // public class CallEntry : DependencyObject
+
+ ///
+ /// Interaction logic for CallHistoryWindow.xaml.
+ ///
public partial class CallHistoryWindow : Window
{
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
public CallHistoryViewModel ViewModel { get; set; }
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
public CallHistoryWindow()
{
InitializeComponent();
@@ -28,12 +107,22 @@ namespace DVMConsole
DataContext = ViewModel;
}
+ ///
+ ///
+ ///
+ ///
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.Hide();
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public void AddCall(string channel, int srcId, int dstId)
{
Dispatcher.Invoke(() =>
@@ -48,6 +137,12 @@ namespace DVMConsole
});
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public void ChannelKeyed(string channel, int srcId, bool encrypted)
{
Dispatcher.Invoke(() =>
@@ -63,6 +158,11 @@ namespace DVMConsole
});
}
+ ///
+ ///
+ ///
+ ///
+ ///
public void ChannelUnkeyed(string channel, int srcId)
{
Dispatcher.Invoke(() =>
@@ -73,31 +173,5 @@ namespace DVMConsole
}
});
}
- }
-
- public class CallHistoryViewModel
- {
- public ObservableCollection CallHistory { get; set; }
-
- public CallHistoryViewModel()
- {
- CallHistory = new ObservableCollection();
- }
- }
-
- public class CallEntry : DependencyObject
- {
- public string Channel { get; set; }
- public int SrcId { get; set; }
- public int DstId { get; set; }
-
- public static readonly DependencyProperty BackgroundColorProperty =
- DependencyProperty.Register(nameof(BackgroundColor), typeof(Brush), typeof(CallEntry), new PropertyMetadata(Brushes.Transparent));
-
- public Brush BackgroundColor
- {
- get { return (Brush)GetValue(BackgroundColorProperty); }
- set { SetValue(BackgroundColorProperty, value); }
- }
- }
-}
+ } // public partial class CallHistoryWindow : Window
+} // namespace dvmconsole
diff --git a/DVMConsole/ChannelPosition.cs b/DVMConsole/ChannelPosition.cs
deleted file mode 100644
index 3c3ff9e..0000000
--- a/DVMConsole/ChannelPosition.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: AGPL-3.0-only
-/**
-* Digital Voice Modem - DVMConsole
-* AGPLv3 Open Source. Use is subject to license terms.
-* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-*
-* @package DVM / DVM Console
-* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
-*
-* Copyright (C) 2025 Caleb, K4PHP
-*
-*/
-
-namespace DVMConsole
-{
- public class ChannelPosition
- {
- public double X { get; set; }
- public double Y { get; set; }
- }
-}
diff --git a/DVMConsole/Codeplug.cs b/DVMConsole/Codeplug.cs
index 41300c3..9fa4371 100644
--- a/DVMConsole/Codeplug.cs
+++ b/DVMConsole/Codeplug.cs
@@ -1,94 +1,206 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2024-2025 Caleb, K4PHP
+* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
-using System.Security.Policy;
+using fnecore.P25;
-namespace DVMConsole
+namespace dvmconsole
{
-
///
- /// Codeplug object used project wide
+ /// Codeplug object used to configure the console.
///
public class Codeplug
{
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
public List Systems { get; set; }
+ ///
+ ///
+ ///
public List Zones { get; set; }
+ /*
+ ** Classes
+ */
+
///
///
///
public class System
{
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
public string Name { get; set; }
+ ///
+ ///
+ ///
public string Identity { get; set; }
+ ///
+ ///
+ ///
public string Address { get; set; }
+ ///
+ ///
+ ///
public string Password { get; set; }
+ ///
+ ///
+ ///
public string PresharedKey { get; set; }
+ ///
+ ///
+ ///
public bool Encrypted { get; set; }
+ ///
+ ///
+ ///
public uint PeerId { get; set; }
+ ///
+ ///
+ ///
public int Port { get; set; }
+ ///
+ ///
+ ///
public string Rid { get; set; }
+ ///
+ ///
+ ///
public string AliasPath { get; set; } = "./alias.yml";
+ ///
+ ///
+ ///
public List RidAlias { get; set; } = null;
+ /*
+ ** Methods
+ */
+
+ ///
+ ///
+ ///
+ ///
public override string ToString()
{
return Name;
}
- }
+ } // public class System
///
///
///
public class Zone
{
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
public string Name { get; set; }
+ ///
+ ///
+ ///
public List Channels { get; set; }
- }
+ } // public class Zone
///
///
///
public class Channel
{
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
public string Name { get; set; }
+ ///
+ ///
+ ///
public string System { get; set; }
+ ///
+ ///
+ ///
public string Tgid { get; set; }
+ ///
+ ///
+ ///
public string EncryptionKey { get; set; }
- public string AlgoId { get; set; } = "0x80";
+ ///
+ ///
+ ///
+ public string Algo { get; set; } = "none";
+ ///
+ ///
+ ///
public string KeyId { get; set; }
+ /*
+ ** Methods
+ */
+
+ ///
+ ///
+ ///
+ ///
public ushort GetKeyId()
{
return Convert.ToUInt16(KeyId, 16);
}
+ ///
+ ///
+ ///
+ ///
public byte GetAlgoId()
{
- return Convert.ToByte(AlgoId, 16);
+ switch (Algo.ToLowerInvariant())
+ {
+ case "aes":
+ return P25Defines.P25_ALGO_AES;
+ case "arc4":
+ return P25Defines.P25_ALGO_ARC4;
+ default:
+ return P25Defines.P25_ALGO_UNENCRYPT;
+ }
}
+ ///
+ ///
+ ///
+ ///
public byte[] GetEncryptionKey()
{
if (EncryptionKey == null)
return [];
- return EncryptionKey
- .Split(',')
- .Select(s => Convert.ToByte(s.Trim(), 16))
- .ToArray();
+ return EncryptionKey.Split(',').Select(s => Convert.ToByte(s.Trim(), 16)).ToArray();
}
- }
+ } // public class Channel
///
/// Helper to return a system by looking up a
@@ -111,10 +223,9 @@ namespace DVMConsole
{
var channel = zone.Channels.FirstOrDefault(c => c.Name == channelName);
if (channel != null)
- {
return Systems.FirstOrDefault(s => s.Name == channel.System);
- }
}
+
return null;
}
@@ -129,11 +240,10 @@ namespace DVMConsole
{
var channel = zone.Channels.FirstOrDefault(c => c.Name == channelName);
if (channel != null)
- {
return channel;
- }
}
+
return null;
}
- }
-}
\ No newline at end of file
+ } //public class Codeplug
+} // namespace dvmconsole
diff --git a/DVMConsole/ConsoleNative.cs b/DVMConsole/ConsoleNative.cs
deleted file mode 100644
index a91db88..0000000
--- a/DVMConsole/ConsoleNative.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: AGPL-3.0-only
-/**
-* Digital Voice Modem - DVMConsole
-* AGPLv3 Open Source. Use is subject to license terms.
-* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-*
-* @package DVM / DVM Console
-* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
-*
-* Copyright (C) 2025 Caleb, K4PHP
-*
-*/
-
-using System.Runtime.InteropServices;
-
-namespace DVMConsole
-{
- public static class ConsoleNative
- {
- [DllImport("kernel32.dll")]
- private static extern bool AllocConsole();
-
- public static void ShowConsole()
- {
- AllocConsole();
- Console.WriteLine("Console attached.");
- }
- }
-}
diff --git a/DVMConsole/DVMConsole.csproj b/DVMConsole/DVMConsole.csproj
index 5c115e0..83efebd 100644
--- a/DVMConsole/DVMConsole.csproj
+++ b/DVMConsole/DVMConsole.csproj
@@ -9,6 +9,9 @@
AnyCPU;x64;x86
Debug;Release;WIN32
true
+ Copyright (c) 2025 Caleb, K4PHP and DVMProject (https://github.com/dvmproject) Authors.
+ x86
+ AGPL-3.0-only
@@ -28,7 +31,6 @@
-
@@ -94,9 +96,6 @@
Always
-
- Always
-
@@ -121,7 +120,7 @@
-
+
diff --git a/DVMConsole/DigitalPageWindow.xaml b/DVMConsole/DigitalPageWindow.xaml
index 5ba7268..5ef98c2 100644
--- a/DVMConsole/DigitalPageWindow.xaml
+++ b/DVMConsole/DigitalPageWindow.xaml
@@ -1,9 +1,9 @@
-
diff --git a/DVMConsole/DigitalPageWindow.xaml.cs b/DVMConsole/DigitalPageWindow.xaml.cs
index 4cb9dc5..014b207 100644
--- a/DVMConsole/DigitalPageWindow.xaml.cs
+++ b/DVMConsole/DigitalPageWindow.xaml.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Caleb, K4PHP
@@ -13,10 +13,10 @@
using System.Windows;
-namespace DVMConsole
+namespace dvmconsole
{
///
- /// Interaction logic for DigitalPageWindow.xaml
+ /// Interaction logic for DigitalPageWindow.xaml.
///
public partial class DigitalPageWindow : Window
{
@@ -25,6 +25,10 @@ namespace DVMConsole
public string DstId = string.Empty;
public Codeplug.System RadioSystem = null;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
public DigitalPageWindow(List systems)
{
InitializeComponent();
@@ -35,6 +39,11 @@ namespace DVMConsole
SystemCombo.SelectedIndex = 0;
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void SendPageButton_Click(object sender, RoutedEventArgs e)
{
RadioSystem = SystemCombo.SelectedItem as Codeplug.System;
@@ -42,5 +51,5 @@ namespace DVMConsole
DialogResult = true;
Close();
}
- }
-}
+ } // public partial class DigitalPageWindow : Window
+} // namespace dvmconsole
diff --git a/DVMConsole/FlashingBackgroundManager.cs b/DVMConsole/FlashingBackgroundManager.cs
index 6a66542..094fa5b 100644
--- a/DVMConsole/FlashingBackgroundManager.cs
+++ b/DVMConsole/FlashingBackgroundManager.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Caleb, K4PHP
@@ -16,96 +16,125 @@ using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
-namespace DVMConsole
+namespace dvmconsole
{
+ ///
+ ///
+ ///
public class FlashingBackgroundManager
{
- private readonly Control _control;
- private readonly Canvas _canvas;
- private readonly UserControl _userControl;
- private readonly Window _mainWindow;
- private readonly DispatcherTimer _timer;
- private Brush _originalControlBackground;
- private Brush _originalCanvasBackground;
- private Brush _originalUserControlBackground;
- private Brush _originalMainWindowBackground;
- private bool _isFlashing;
-
+ private readonly Control control;
+ private readonly Canvas canvas;
+ private readonly UserControl userControl;
+ private readonly Window mainWindow;
+ private readonly DispatcherTimer timer;
+
+ private Brush originalControlBackground;
+ private Brush originalCanvasBackground;
+ private Brush originalUserControlBackground;
+ private Brush originalMainWindowBackground;
+
+ private bool isFlashing;
+
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public FlashingBackgroundManager(Control control = null, Canvas canvas = null, UserControl userControl = null, Window mainWindow = null, int intervalMilliseconds = 450)
{
- _control = control;
- _canvas = canvas;
- _userControl = userControl;
- _mainWindow = mainWindow;
+ this.control = control;
+ this.canvas = canvas;
+ this.userControl = userControl;
+ this.mainWindow = mainWindow;
- if (_control == null && _canvas == null && _userControl == null && _mainWindow == null)
+ if (this.control == null && this.canvas == null && this.userControl == null && this.mainWindow == null)
throw new ArgumentException("At least one of control, canvas, userControl, or mainWindow must be provided.");
- _timer = new DispatcherTimer
+ timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(intervalMilliseconds)
};
- _timer.Tick += OnTimerTick;
+ timer.Tick += OnTimerTick;
}
+ ///
+ ///
+ ///
public void Start()
{
- if (_isFlashing)
+ if (isFlashing)
return;
- if (_control != null)
- _originalControlBackground = _control.Background;
+ if (control != null)
+ originalControlBackground = control.Background;
- if (_canvas != null)
- _originalCanvasBackground = _canvas.Background;
+ if (canvas != null)
+ originalCanvasBackground = canvas.Background;
- if (_userControl != null)
- _originalUserControlBackground = _userControl.Background;
+ if (userControl != null)
+ originalUserControlBackground = userControl.Background;
- if (_mainWindow != null)
- _originalMainWindowBackground = _mainWindow.Background;
+ if (mainWindow != null)
+ originalMainWindowBackground = mainWindow.Background;
- _isFlashing = true;
- _timer.Start();
+ isFlashing = true;
+ timer.Start();
}
-
+
+ ///
+ ///
+ ///
public void Stop()
{
- if (!_isFlashing)
+ if (!isFlashing)
return;
- _timer.Stop();
+ timer.Stop();
- if (_control != null)
- _control.Background = _originalControlBackground;
+ if (control != null)
+ control.Background = originalControlBackground;
- if (_canvas != null)
- _canvas.Background = _originalCanvasBackground;
+ if (canvas != null)
+ canvas.Background = originalCanvasBackground;
- if (_userControl != null)
- _userControl.Background = _originalUserControlBackground;
+ if (userControl != null)
+ userControl.Background = originalUserControlBackground;
- if (_mainWindow != null && _originalMainWindowBackground != null)
- _mainWindow.Background = _originalMainWindowBackground;
+ if (mainWindow != null && originalMainWindowBackground != null)
+ mainWindow.Background = originalMainWindowBackground;
- _isFlashing = false;
+ isFlashing = false;
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void OnTimerTick(object sender, EventArgs e)
{
Brush flashingColor = Brushes.Red;
- if (_control != null)
- _control.Background = _control.Background == Brushes.DarkRed ? _originalControlBackground : Brushes.DarkRed;
+ if (control != null)
+ control.Background = control.Background == Brushes.DarkRed ? originalControlBackground : Brushes.DarkRed;
- if (_canvas != null)
- _canvas.Background = _canvas.Background == flashingColor ? _originalCanvasBackground : flashingColor;
+ if (canvas != null)
+ canvas.Background = canvas.Background == flashingColor ? originalCanvasBackground : flashingColor;
- if (_userControl != null)
- _userControl.Background = _userControl.Background == Brushes.DarkRed ? _originalUserControlBackground : Brushes.DarkRed;
+ if (userControl != null)
+ userControl.Background = userControl.Background == Brushes.DarkRed ? originalUserControlBackground : Brushes.DarkRed;
- if (_mainWindow != null)
- _mainWindow.Background = _mainWindow.Background == flashingColor ? _originalMainWindowBackground : flashingColor;
+ if (mainWindow != null)
+ mainWindow.Background = mainWindow.Background == flashingColor ? originalMainWindowBackground : flashingColor;
}
- }
-}
+ } // public class FlashingBackgroundManager
+} // namespace dvmconsole
diff --git a/DVMConsole/FneSystemBase.DMR.cs b/DVMConsole/FneSystemBase.DMR.cs
index 1a85812..a782e91 100644
--- a/DVMConsole/FneSystemBase.DMR.cs
+++ b/DVMConsole/FneSystemBase.DMR.cs
@@ -11,12 +11,12 @@
*
*/
-using fnecore.DMR;
using fnecore;
+using fnecore.DMR;
using NAudio.Wave;
-namespace DVMConsole
+namespace dvmconsole
{
///
/// Implements a FNE system base.
@@ -113,4 +113,4 @@ namespace DVMConsole
return;
}
} // public abstract partial class FneSystemBase : fnecore.FneSystemBase
-}
\ No newline at end of file
+} // namespace dvmconsole
diff --git a/DVMConsole/FneSystemBase.NXDN.cs b/DVMConsole/FneSystemBase.NXDN.cs
index 99301d9..10b312c 100644
--- a/DVMConsole/FneSystemBase.NXDN.cs
+++ b/DVMConsole/FneSystemBase.NXDN.cs
@@ -11,10 +11,10 @@
*
*/
-using fnecore.NXDN;
using fnecore;
+using fnecore.NXDN;
-namespace DVMConsole
+namespace dvmconsole
{
///
/// Implements a FNE system base.
@@ -54,4 +54,4 @@ namespace DVMConsole
return;
}
} // public abstract partial class FneSystemBase : fnecore.FneSystemBase
-}
+} // namespace dvmconsole
diff --git a/DVMConsole/FneSystemBase.P25.cs b/DVMConsole/FneSystemBase.P25.cs
index 67e4fe8..b4e40c5 100644
--- a/DVMConsole/FneSystemBase.P25.cs
+++ b/DVMConsole/FneSystemBase.P25.cs
@@ -15,8 +15,31 @@
using fnecore;
using fnecore.P25;
-namespace DVMConsole
+namespace dvmconsole
{
+ ///
+ ///
+ ///
+ public class CryptoParams
+ {
+ /*
+ ** Properties
+ */
+
+ ///
+ /// Message Indicator
+ ///
+ public byte[] MI { get; set; } = new byte[P25Defines.P25_MI_LENGTH];
+ ///
+ /// Algorithm ID.
+ ///
+ public byte AlgId { get; set; } = P25Defines.P25_ALGO_UNENCRYPT;
+ ///
+ /// Key ID.
+ ///
+ public ushort KeyId { get; set; }
+ } // public class CryptoParams
+
///
/// Implements a FNE system base.
///
@@ -55,11 +78,20 @@ namespace DVMConsole
return;
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public void CreateNewP25MessageHdr(byte duid, RemoteCallData callData, ref byte[] data, byte algId = 0, ushort kId = 0, byte[] mi = null)
{
CreateP25MessageHdr(duid, callData, ref data);
- // if an mi is present, this is an encrypted header
+ // if an MI is present, this is an encrypted header
if (mi != null)
{
data[14U] |= 0x08; // Control bit
@@ -329,25 +361,25 @@ namespace DVMConsole
break;
case P25DFSI.P25_DFSI_LDU2_VOICE12:
{
- dfsiFrame[1U] = cryptoParams.Mi[0]; // Message Indicator
- dfsiFrame[2U] = cryptoParams.Mi[1];
- dfsiFrame[3U] = cryptoParams.Mi[2];
+ dfsiFrame[1U] = cryptoParams.MI[0]; // Message Indicator
+ dfsiFrame[2U] = cryptoParams.MI[1];
+ dfsiFrame[3U] = cryptoParams.MI[2];
Buffer.BlockCopy(imbe, 0, dfsiFrame, 5, IMBE_BUF_LEN); // IMBE
}
break;
case P25DFSI.P25_DFSI_LDU2_VOICE13:
{
- dfsiFrame[1U] = cryptoParams.Mi[3]; // Message Indicator
- dfsiFrame[2U] = cryptoParams.Mi[4];
- dfsiFrame[3U] = cryptoParams.Mi[5];
+ dfsiFrame[1U] = cryptoParams.MI[3]; // Message Indicator
+ dfsiFrame[2U] = cryptoParams.MI[4];
+ dfsiFrame[3U] = cryptoParams.MI[5];
Buffer.BlockCopy(imbe, 0, dfsiFrame, 5, IMBE_BUF_LEN); // IMBE
}
break;
case P25DFSI.P25_DFSI_LDU2_VOICE14:
{
- dfsiFrame[1U] = cryptoParams.Mi[6]; // Message Indicator
- dfsiFrame[2U] = cryptoParams.Mi[7];
- dfsiFrame[3U] = cryptoParams.Mi[8];
+ dfsiFrame[1U] = cryptoParams.MI[6]; // Message Indicator
+ dfsiFrame[2U] = cryptoParams.MI[7];
+ dfsiFrame[3U] = cryptoParams.MI[8];
Buffer.BlockCopy(imbe, 0, dfsiFrame, 5, IMBE_BUF_LEN); // IMBE
}
break;
@@ -468,14 +500,4 @@ namespace DVMConsole
}
}
} // public abstract partial class FneSystemBase : fnecore.FneSystemBase
-
- ///
- ///
- ///
- public class CryptoParams
- {
- public byte[] Mi { get; set; } = new byte[P25Defines.P25_MI_LENGTH];
- public byte AlgId { get; set; } = P25Defines.P25_ALGO_UNENCRYPT;
- public ushort KeyId { get; set; }
- }
-}
\ No newline at end of file
+} // namespace dvmconsole
diff --git a/DVMConsole/FneSystemBase.cs b/DVMConsole/FneSystemBase.cs
index f3ea2f0..8955d5e 100644
--- a/DVMConsole/FneSystemBase.cs
+++ b/DVMConsole/FneSystemBase.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2022-2024 Bryan Biedenkapp, N2PLL
@@ -12,11 +12,11 @@
*
*/
-using fnecore.DMR;
using fnecore;
+using fnecore.DMR;
using fnecore.P25.kmm;
-namespace DVMConsole
+namespace dvmconsole
{
///
/// Represents the individual timeslot data status.
@@ -196,11 +196,8 @@ namespace DVMConsole
byte[] payload = e.Data.Skip(11).ToArray();
//Console.WriteLine(FneUtils.HexDump(payload));
-
if (e.MessageId == (byte)KmmMessageType.MODIFY_KEY_CMD)
- {
mainWindow.KeyResponseReceived(e);
- }
}
///
@@ -213,4 +210,4 @@ namespace DVMConsole
}
} // public abstract partial class FneSystemBase : fnecore.FneSystemBase
-}
\ No newline at end of file
+} // namespace dvmconsole
diff --git a/DVMConsole/FneSystemManager.cs b/DVMConsole/FneSystemManager.cs
index 356aa89..cadceaf 100644
--- a/DVMConsole/FneSystemManager.cs
+++ b/DVMConsole/FneSystemManager.cs
@@ -1,31 +1,35 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Caleb, K4PHP
*
*/
-namespace DVMConsole
+namespace dvmconsole
{
///
- /// WhackerLink peer/client websocket manager for having multiple systems
+ ///
///
public class FneSystemManager
{
- private readonly Dictionary _webSocketHandlers;
+ private readonly Dictionary peerHandlers;
+
+ /*
+ ** Methods
+ */
///
- /// Creates an instance of
+ /// Creates an instance of class.
///
public FneSystemManager()
{
- _webSocketHandlers = new Dictionary();
+ peerHandlers = new Dictionary();
}
///
@@ -34,10 +38,8 @@ namespace DVMConsole
///
public void AddFneSystem(string systemId, Codeplug.System system, MainWindow mainWindow)
{
- if (!_webSocketHandlers.ContainsKey(systemId))
- {
- _webSocketHandlers[systemId] = new PeerSystem(mainWindow, system);
- }
+ if (!peerHandlers.ContainsKey(systemId))
+ peerHandlers[systemId] = new PeerSystem(mainWindow, system);
}
///
@@ -48,10 +50,9 @@ namespace DVMConsole
///
public PeerSystem GetFneSystem(string systemId)
{
- if (_webSocketHandlers.TryGetValue(systemId, out var handler))
- {
+ if (peerHandlers.TryGetValue(systemId, out var handler))
return handler;
- }
+
throw new KeyNotFoundException($"WebSocketHandler for system '{systemId}' not found.");
}
@@ -61,10 +62,10 @@ namespace DVMConsole
///
public void RemoveFneSystem(string systemId)
{
- if (_webSocketHandlers.TryGetValue(systemId, out var handler))
+ if (peerHandlers.TryGetValue(systemId, out var handler))
{
handler.peer.Stop();
- _webSocketHandlers.Remove(systemId);
+ peerHandlers.Remove(systemId);
}
}
@@ -75,7 +76,7 @@ namespace DVMConsole
///
public bool HasFneSystem(string systemId)
{
- return _webSocketHandlers.ContainsKey(systemId);
+ return peerHandlers.ContainsKey(systemId);
}
///
@@ -83,11 +84,10 @@ namespace DVMConsole
///
public void ClearAll()
{
- foreach (var handler in _webSocketHandlers.Values)
- {
+ foreach (var handler in peerHandlers.Values)
handler.peer.Stop();
- }
- _webSocketHandlers.Clear();
+
+ peerHandlers.Clear();
}
- }
-}
+ } // public class FneSystemManager
+} // namespace dvmconsole
diff --git a/DVMConsole/GainSampleProvider.cs b/DVMConsole/GainSampleProvider.cs
index 06dfff4..85d5229 100644
--- a/DVMConsole/GainSampleProvider.cs
+++ b/DVMConsole/GainSampleProvider.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Caleb, K4PHP
@@ -12,40 +12,64 @@
*/
using NAudio.Wave;
-using NAudio.Wave.SampleProviders;
-using System;
-namespace DVMConsole
+namespace dvmconsole
{
+ ///
+ ///
+ ///
public class GainSampleProvider : ISampleProvider
{
- private readonly ISampleProvider _source;
- private float _gain = 1.0f;
+ private readonly ISampleProvider source;
+ private float gain = 1.0f;
- public GainSampleProvider(ISampleProvider source)
- {
- _source = source ?? throw new ArgumentNullException(nameof(source));
- WaveFormat = source.WaveFormat;
- }
+ /*
+ ** Properties
+ */
+ ///
+ ///
+ ///
public WaveFormat WaveFormat { get; }
+ ///
+ ///
+ ///
public float Gain
{
- get => _gain;
- set => _gain = Math.Max(0, value);
+ get => gain;
+ set => gain = Math.Max(0, value);
}
- public int Read(float[] buffer, int offset, int count)
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ public GainSampleProvider(ISampleProvider source)
{
- int samplesRead = _source.Read(buffer, offset, count);
+ this.source = source ?? throw new ArgumentNullException(nameof(source));
+ WaveFormat = source.WaveFormat;
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public int Read(float[] buffer, int offset, int count)
+ {
+ int samplesRead = source.Read(buffer, offset, count);
for (int i = 0; i < samplesRead; i++)
- {
- buffer[offset + i] *= _gain;
- }
+ buffer[offset + i] *= gain;
return samplesRead;
}
- }
-}
+ } // public class GainSampleProvider : ISampleProvider
+} // namespace dvmconsole
diff --git a/DVMConsole/KeyStatusWindow.xaml b/DVMConsole/KeyStatusWindow.xaml
index fa9f55f..fbc4c32 100644
--- a/DVMConsole/KeyStatusWindow.xaml
+++ b/DVMConsole/KeyStatusWindow.xaml
@@ -1,4 +1,4 @@
-
+ ///
+ ///
+ public class KeyStatusItem
{
- public ObservableCollection KeyStatusItems { get; private set; } = new ObservableCollection();
+ /*
+ ** Properties
+ */
+ ///
+ ///
+ ///
+ public string ChannelName { get; set; }
+ ///
+ ///
+ ///
+ public string AlgId { get; set; }
+ ///
+ ///
+ ///
+ public string KeyId { get; set; }
+ ///
+ ///
+ ///
+ public string KeyStatus { get; set; }
+ } // public class KeyStatusItem
+
+ ///
+ ///
+ ///
+ public partial class KeyStatusWindow : Window
+ {
private Codeplug Codeplug;
private MainWindow mainWindow;
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
+ public ObservableCollection KeyStatusItems { get; private set; } = new ObservableCollection();
+
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
public KeyStatusWindow(Codeplug codeplug, MainWindow mainWindow)
{
InitializeComponent();
@@ -34,6 +82,9 @@ namespace DVMConsole
LoadKeyStatus();
}
+ ///
+ ///
+ ///
private void LoadKeyStatus()
{
Dispatcher.Invoke(() =>
@@ -44,7 +95,7 @@ namespace DVMConsole
{
if (child == null)
{
- Console.WriteLine("A child in ChannelsCanvas.Children is null.");
+ Trace.WriteLine("A child in ChannelsCanvas.Children is null.");
continue;
}
@@ -56,14 +107,14 @@ namespace DVMConsole
Codeplug.System system = Codeplug.GetSystemForChannel(channelBox.ChannelName);
if (system == null)
{
- Console.WriteLine($"System not found for {channelBox.ChannelName}");
+ Trace.WriteLine($"System not found for {channelBox.ChannelName}");
continue;
}
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channelBox.ChannelName);
if (cpgChannel == null)
{
- Console.WriteLine($"Channel not found for {channelBox.ChannelName}");
+ Trace.WriteLine($"Channel not found for {channelBox.ChannelName}");
continue;
}
@@ -72,7 +123,7 @@ namespace DVMConsole
if (channelBox.crypter == null)
{
- Console.WriteLine($"Crypter is null for channel {channelBox.ChannelName}");
+ Trace.WriteLine($"Crypter is null for channel {channelBox.ChannelName}");
continue;
}
@@ -88,13 +139,5 @@ namespace DVMConsole
}
});
}
- }
-
- public class KeyStatusItem
- {
- public string ChannelName { get; set; }
- public string AlgId { get; set; }
- public string KeyId { get; set; }
- public string KeyStatus { get; set; }
- }
-}
+ } // public partial class KeyStatusWindow : Window
+} // namespace dvmconsole
diff --git a/DVMConsole/MBEToneDetector.cs b/DVMConsole/MBEToneDetector.cs
index 122e90b..9e0b885 100644
--- a/DVMConsole/MBEToneDetector.cs
+++ b/DVMConsole/MBEToneDetector.cs
@@ -1,15 +1,24 @@
-// From github.com/w3axl/rc2-dvm
+// SPDX-License-Identifier: AGPL-3.0-only
+/**
+* Digital Voice Modem - Desktop Dispatch Console
+* AGPLv3 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Desktop Dispatch Console
+* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
+*
+* Copyright (C) 2025 Patrick McDonnell, W3AXL
+*
+*/
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using NWaves.Signals;
using NWaves.Transforms;
-namespace DVMConsole
+namespace dvmconsole
{
+ ///
+ ///
+ ///
public class MBEToneDetector
{
// Samplerate is 8000 Hz
@@ -41,6 +50,10 @@ namespace DVMConsole
// The STFT (short-time fourier transform) operator
private Stft stft;
+ /*
+ ** Methods
+ */
+
///
/// Create a pitch detector which reports the running average of pitch for a sequence of samples
///
@@ -123,5 +136,5 @@ namespace DVMConsole
}
return 0;
}
- }
-}
+ } // public class MBEToneDetector
+} // namespace dvmconsole
diff --git a/DVMConsole/MainWindow.xaml b/DVMConsole/MainWindow.xaml
index 50ef77a..8868269 100644
--- a/DVMConsole/MainWindow.xaml
+++ b/DVMConsole/MainWindow.xaml
@@ -1,8 +1,8 @@
-
+ xmlns:local="clr-namespace:dvmconsole.Controls"
+ Title="Digital Voice Modem - Desktop Dispatch Console" Height="600" Width="1000" Background="#FFF2F2F2">
diff --git a/DVMConsole/MainWindow.xaml.cs b/DVMConsole/MainWindow.xaml.cs
index 7a70298..0c212e4 100644
--- a/DVMConsole/MainWindow.xaml.cs
+++ b/DVMConsole/MainWindow.xaml.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2024-2025 Caleb, K4PHP
@@ -12,46 +12,73 @@
*
*/
-using Microsoft.Win32;
-using System;
-using System.Timers;
+using System.Diagnostics;
using System.IO;
+using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
-using YamlDotNet.Serialization;
-using YamlDotNet.Serialization.NamingConventions;
-using DVMConsole.Controls;
using System.Windows.Media;
+
+using Microsoft.Win32;
+
using NAudio.Wave;
-using fnecore.P25;
-using fnecore;
+using NWaves.Signals;
+
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
+
+using dvmconsole.Controls;
+using static dvmconsole.P25Crypto;
+
using Constants = fnecore.Constants;
+using fnecore;
+using fnecore.P25;
using fnecore.P25.LC.TSBK;
-using NWaves.Signals;
-using static DVMConsole.P25Crypto;
-namespace DVMConsole
+
+namespace dvmconsole
{
+ ///
+ ///
+ ///
+ public class ChannelPosition
+ {
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
+ public double X { get; set; }
+ ///
+ ///
+ ///
+ public double Y { get; set; }
+ } // public class ChannelPosition
+
+ ///
+ /// Interaction logic for MainWindow.xaml.
+ ///
public partial class MainWindow : Window
{
- public Codeplug Codeplug { get; set; }
private bool isEditMode = false;
private bool globalPttState = false;
private const int GridSize = 5;
- private UIElement _draggedElement;
- private Point _startPoint;
- private double _offsetX;
- private double _offsetY;
- private bool _isDragging;
+ private UIElement draggedElement;
+ private Point startPoint;
+ private double offsetX;
+ private double offsetY;
+ private bool isDragging;
- private SettingsManager _settingsManager = new SettingsManager();
- private SelectedChannelsManager _selectedChannelsManager;
- private FlashingBackgroundManager _flashingManager;
- private WaveFilePlaybackManager _emergencyAlertPlayback;
+ private SettingsManager settingsManager = new SettingsManager();
+ private SelectedChannelsManager selectedChannelsManager;
+ private FlashingBackgroundManager flashingManager;
+ private WaveFilePlaybackManager emergencyAlertPlayback;
private ChannelBox playbackChannelBox;
@@ -61,45 +88,60 @@ namespace DVMConsole
public static string PLAYBACKSYS = "LOCPLAYBACKSYS";
public static string PLAYBACKCHNAME = "PLAYBACK";
- private readonly WaveInEvent _waveIn;
- private readonly AudioManager _audioManager;
+ private readonly WaveInEvent waveIn;
+ private readonly AudioManager audioManager;
- private static System.Timers.Timer _channelHoldTimer;
+ private static System.Timers.Timer channelHoldTimer;
private Dictionary systemStatuses = new Dictionary();
- private FneSystemManager _fneSystemManager = new FneSystemManager();
+ private FneSystemManager fneSystemManager = new FneSystemManager();
+
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
+ public Codeplug Codeplug { get; set; }
+
+ /*
+ ** Methods
+ */
+ ///
+ /// Initializes a new instance of the class.
+ ///
public MainWindow()
{
-#if !DEBUG
- ConsoleNative.ShowConsole();
-#endif
InitializeComponent();
- _settingsManager.LoadSettings();
- _selectedChannelsManager = new SelectedChannelsManager();
- _flashingManager = new FlashingBackgroundManager(null, ChannelsCanvas, null, this);
- _emergencyAlertPlayback = new WaveFilePlaybackManager(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "emergency.wav"));
+ settingsManager.LoadSettings();
+ selectedChannelsManager = new SelectedChannelsManager();
+ flashingManager = new FlashingBackgroundManager(null, ChannelsCanvas, null, this);
+ emergencyAlertPlayback = new WaveFilePlaybackManager(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "emergency.wav"));
- _channelHoldTimer = new System.Timers.Timer(10000);
- _channelHoldTimer.Elapsed += OnHoldTimerElapsed;
- _channelHoldTimer.AutoReset = true;
- _channelHoldTimer.Enabled = true;
+ channelHoldTimer = new System.Timers.Timer(10000);
+ channelHoldTimer.Elapsed += OnHoldTimerElapsed;
+ channelHoldTimer.AutoReset = true;
+ channelHoldTimer.Enabled = true;
- _waveIn = new WaveInEvent
- {
- WaveFormat = new WaveFormat(8000, 16, 1)
- };
- _waveIn.DataAvailable += WaveIn_DataAvailable;
- _waveIn.RecordingStopped += WaveIn_RecordingStopped;
+ waveIn = new WaveInEvent { WaveFormat = new WaveFormat(8000, 16, 1) };
+ waveIn.DataAvailable += WaveIn_DataAvailable;
+ waveIn.RecordingStopped += WaveIn_RecordingStopped;
- _waveIn.StartRecording();
+ waveIn.StartRecording();
- _audioManager = new AudioManager(_settingsManager);
+ audioManager = new AudioManager(settingsManager);
- _selectedChannelsManager.SelectedChannelsChanged += SelectedChannelsChanged;
+ selectedChannelsManager.SelectedChannelsChanged += SelectedChannelsChanged;
Loaded += MainWindow_Loaded;
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void OpenCodeplug_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog
@@ -107,21 +149,31 @@ namespace DVMConsole
Filter = "Codeplug Files (*.yml)|*.yml|All Files (*.*)|*.*",
Title = "Open Codeplug"
};
+
if (openFileDialog.ShowDialog() == true)
{
LoadCodeplug(openFileDialog.FileName);
- _settingsManager.LastCodeplugPath = openFileDialog.FileName;
- _settingsManager.SaveSettings();
+ settingsManager.LastCodeplugPath = openFileDialog.FileName;
+ settingsManager.SaveSettings();
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void ResetSettings_Click(object sender, RoutedEventArgs e)
{
if (File.Exists("UserSettings.json"))
File.Delete("UserSettings.json");
}
+ ///
+ ///
+ ///
+ ///
private void LoadCodeplug(string filePath)
{
try
@@ -142,6 +194,9 @@ namespace DVMConsole
}
}
+ ///
+ ///
+ ///
private void GenerateChannelWidgets()
{
ChannelsCanvas.Children.Clear();
@@ -154,7 +209,7 @@ namespace DVMConsole
{
var systemStatusBox = new SystemStatusBox(system.Name, system.Address, system.Port);
- if (_settingsManager.SystemStatusPositions.TryGetValue(system.Name, out var position))
+ if (settingsManager.SystemStatusPositions.TryGetValue(system.Name, out var position))
{
Canvas.SetLeft(systemStatusBox, position.X);
Canvas.SetTop(systemStatusBox, position.Y);
@@ -181,9 +236,9 @@ namespace DVMConsole
if (File.Exists(system.AliasPath))
system.RidAlias = AliasTools.LoadAliases(system.AliasPath);
- _fneSystemManager.AddFneSystem(system.Name, system, this);
+ fneSystemManager.AddFneSystem(system.Name, system, this);
- PeerSystem peer = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem peer = fneSystemManager.GetFneSystem(system.Name);
peer.peer.PeerConnected += (sender, response) =>
{
@@ -213,25 +268,25 @@ namespace DVMConsole
peer.Start();
});
- if (!_settingsManager.ShowSystemStatus)
+ if (!settingsManager.ShowSystemStatus)
systemStatusBox.Visibility = Visibility.Collapsed;
}
}
- if (_settingsManager.ShowChannels && Codeplug != null)
+ if (settingsManager.ShowChannels && Codeplug != null)
{
foreach (var zone in Codeplug.Zones)
{
foreach (var channel in zone.Channels)
{
- var channelBox = new ChannelBox(_selectedChannelsManager, _audioManager, channel.Name, channel.System, channel.Tgid);
+ var channelBox = new ChannelBox(selectedChannelsManager, audioManager, channel.Name, channel.System, channel.Tgid);
//channelBox.crypter.AddKey(channel.GetKeyId(), channel.GetAlgoId(), channel.GetEncryptionKey());
systemStatuses.Add(channel.Name, new SlotStatus());
- if (_settingsManager.ChannelPositions.TryGetValue(channel.Name, out var position))
+ if (settingsManager.ChannelPositions.TryGetValue(channel.Name, out var position))
{
Canvas.SetLeft(channelBox, position.X);
Canvas.SetTop(channelBox, position.Y);
@@ -262,18 +317,14 @@ namespace DVMConsole
}
}
- if (_settingsManager.ShowAlertTones && Codeplug != null)
+ if (settingsManager.ShowAlertTones && Codeplug != null)
{
- foreach (var alertPath in _settingsManager.AlertToneFilePaths)
+ foreach (var alertPath in settingsManager.AlertToneFilePaths)
{
- var alertTone = new AlertTone(alertPath)
- {
- IsEditMode = isEditMode
- };
-
+ var alertTone = new AlertTone(alertPath) { IsEditMode = isEditMode };
alertTone.OnAlertTone += SendAlertTone;
- if (_settingsManager.AlertTonePositions.TryGetValue(alertPath, out var position))
+ if (settingsManager.AlertTonePositions.TryGetValue(alertPath, out var position))
{
Canvas.SetLeft(alertTone, position.X);
Canvas.SetTop(alertTone, position.Y);
@@ -290,9 +341,9 @@ namespace DVMConsole
}
}
- playbackChannelBox = new ChannelBox(_selectedChannelsManager, _audioManager, PLAYBACKCHNAME, PLAYBACKSYS, PLAYBACKTG);
+ playbackChannelBox = new ChannelBox(selectedChannelsManager, audioManager, PLAYBACKCHNAME, PLAYBACKSYS, PLAYBACKTG);
- if (_settingsManager.ChannelPositions.TryGetValue(PLAYBACKCHNAME, out var pos))
+ if (settingsManager.ChannelPositions.TryGetValue(PLAYBACKCHNAME, out var pos))
{
Canvas.SetLeft(playbackChannelBox, pos.X);
Canvas.SetTop(playbackChannelBox, pos.Y);
@@ -323,16 +374,26 @@ namespace DVMConsole
AdjustCanvasHeight();
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void WaveIn_RecordingStopped(object sender, EventArgs e)
{
/* stub */
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void WaveIn_DataAvailable(object sender, WaveInEventArgs e)
{
bool isAnyTgOn = false;
- foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
{
if (channel.SystemName == PLAYBACKSYS || channel.ChannelName == PLAYBACKCHNAME || channel.DstId == PLAYBACKTG)
{
@@ -345,7 +406,7 @@ namespace DVMConsole
Task.Run(() =>
{
- PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem handler = fneSystemManager.GetFneSystem(system.Name);
if (channel.IsSelected && channel.PttState)
{
@@ -358,25 +419,24 @@ namespace DVMConsole
foreach (byte[] chunk in channel.chunkedPcm)
{
if (chunk.Length == samples)
- {
P25EncodeAudioFrame(chunk, handler, channel, cpgChannel, system);
- }
else
- {
- Console.WriteLine("bad sample length: " + chunk.Length);
- }
+ Trace.WriteLine("bad sample length: " + chunk.Length);
}
}
});
}
if (isAnyTgOn && playbackChannelBox.IsSelected)
- _audioManager.AddTalkgroupStream(PLAYBACKTG, e.Buffer);
+ audioManager.AddTalkgroupStream(PLAYBACKTG, e.Buffer);
}
+ ///
+ ///
+ ///
private void SelectedChannelsChanged()
{
- foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
{
if (channel.SystemName == PLAYBACKSYS || channel.ChannelName == PLAYBACKCHNAME || channel.DstId == PLAYBACKTG)
continue;
@@ -384,7 +444,7 @@ namespace DVMConsole
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
- PeerSystem fne = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem fne = fneSystemManager.GetFneSystem(system.Name);
if (channel.IsSelected)
{
@@ -396,27 +456,37 @@ namespace DVMConsole
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void AudioSettings_Click(object sender, RoutedEventArgs e)
{
List channels = Codeplug?.Zones.SelectMany(z => z.Channels).ToList() ?? new List();
- AudioSettingsWindow audioSettingsWindow = new AudioSettingsWindow(_settingsManager, _audioManager, channels);
+ AudioSettingsWindow audioSettingsWindow = new AudioSettingsWindow(settingsManager, audioManager, channels);
audioSettingsWindow.ShowDialog();
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void P25Page_Click(object sender, RoutedEventArgs e)
{
DigitalPageWindow pageWindow = new DigitalPageWindow(Codeplug.Systems);
pageWindow.Owner = this;
if (pageWindow.ShowDialog() == true)
{
- PeerSystem handler = _fneSystemManager.GetFneSystem(pageWindow.RadioSystem.Name);
- IOSP_CALL_ALRT callAlert = new IOSP_CALL_ALRT(UInt32.Parse(pageWindow.DstId), UInt32.Parse(pageWindow.RadioSystem.Rid));
+ PeerSystem handler = fneSystemManager.GetFneSystem(pageWindow.RadioSystem.Name);
+ IOSP_CALL_ALRT callAlert = new IOSP_CALL_ALRT(uint.Parse(pageWindow.DstId), uint.Parse(pageWindow.RadioSystem.Rid));
RemoteCallData callData = new RemoteCallData
{
- SrcId = UInt32.Parse(pageWindow.RadioSystem.Rid),
- DstId = UInt32.Parse(pageWindow.DstId),
+ SrcId = uint.Parse(pageWindow.RadioSystem.Rid),
+ DstId = uint.Parse(pageWindow.DstId),
LCO = P25Defines.TSBK_IOSP_CALL_ALRT
};
@@ -431,17 +501,22 @@ namespace DVMConsole
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private async void ManualPage_Click(object sender, RoutedEventArgs e)
{
QuickCallPage pageWindow = new QuickCallPage();
pageWindow.Owner = this;
if (pageWindow.ShowDialog() == true)
{
- foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
{
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
- PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem handler = fneSystemManager.GetFneSystem(system.Name);
if (channel.PageState)
{
@@ -464,7 +539,7 @@ namespace DVMConsole
Task.Run(() =>
{
//_waveProvider.ClearBuffer();
- _audioManager.AddTalkgroupStream(cpgChannel.Tgid, combinedAudio);
+ audioManager.AddTalkgroupStream(cpgChannel.Tgid, combinedAudio);
});
await Task.Run(() =>
@@ -478,16 +553,14 @@ namespace DVMConsole
Buffer.BlockCopy(combinedAudio, offset, chunk, 0, size);
if (chunk.Length == 320)
- {
P25EncodeAudioFrame(chunk, handler, channel, cpgChannel, system);
- }
}
});
double totalDurationMs = (toneADuration + toneBDuration) * 1000 + 750;
await Task.Delay((int)totalDurationMs + 4000);
- handler.SendP25TDU(UInt32.Parse(system.Rid), UInt32.Parse(cpgChannel.Tgid), false);
+ handler.SendP25TDU(uint.Parse(system.Rid), uint.Parse(cpgChannel.Tgid), false);
Dispatcher.Invoke(() =>
{
@@ -499,25 +572,34 @@ namespace DVMConsole
}
}
+ ///
+ ///
+ ///
+ ///
private void SendAlertTone(AlertTone e)
{
Task.Run(() => SendAlertTone(e.AlertFilePath));
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void SendAlertTone(string filePath, bool forHold = false)
{
if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
{
try
{
- foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
{
if (channel.SystemName == PLAYBACKSYS || channel.ChannelName == PLAYBACKCHNAME || channel.DstId == PLAYBACKTG)
continue;
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
- PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem handler = fneSystemManager.GetFneSystem(system.Name);
if (channel.PageState || (forHold && channel.HoldState))
{
@@ -554,7 +636,7 @@ namespace DVMConsole
Task.Run(() =>
{
- _audioManager.AddTalkgroupStream(cpgChannel.Tgid, pcmData);
+ audioManager.AddTalkgroupStream(cpgChannel.Tgid, pcmData);
});
DateTime startTime = DateTime.UtcNow;
@@ -565,31 +647,27 @@ namespace DVMConsole
byte[] chunk = new byte[chunkSize];
Buffer.BlockCopy(pcmData, offset, chunk, 0, chunkSize);
- PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem handler = fneSystemManager.GetFneSystem(system.Name);
channel.chunkedPcm = AudioConverter.SplitToChunks(chunk);
foreach (byte[] smallchunk in channel.chunkedPcm)
{
if (smallchunk.Length == 320)
- {
P25EncodeAudioFrame(smallchunk, handler, channel, cpgChannel, system);
- }
}
DateTime nextPacketTime = startTime.AddMilliseconds((i + 1) * 100);
TimeSpan waitTime = nextPacketTime - DateTime.UtcNow;
if (waitTime.TotalMilliseconds > 0)
- {
await Task.Delay(waitTime);
- }
}
double totalDurationMs = ((double)pcmData.Length / 16000) + 250;
await Task.Delay((int)totalDurationMs + 3000);
- handler.SendP25TDU(UInt32.Parse(system.Rid), UInt32.Parse(cpgChannel.Tgid), false);
+ handler.SendP25TDU(uint.Parse(system.Rid), uint.Parse(cpgChannel.Tgid), false);
Dispatcher.Invoke(() =>
{
@@ -613,26 +691,36 @@ namespace DVMConsole
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void SelectWidgets_Click(object sender, RoutedEventArgs e)
{
WidgetSelectionWindow widgetSelectionWindow = new WidgetSelectionWindow();
widgetSelectionWindow.Owner = this;
if (widgetSelectionWindow.ShowDialog() == true)
{
- _settingsManager.ShowSystemStatus = widgetSelectionWindow.ShowSystemStatus;
- _settingsManager.ShowChannels = widgetSelectionWindow.ShowChannels;
- _settingsManager.ShowAlertTones = widgetSelectionWindow.ShowAlertTones;
+ settingsManager.ShowSystemStatus = widgetSelectionWindow.ShowSystemStatus;
+ settingsManager.ShowChannels = widgetSelectionWindow.ShowChannels;
+ settingsManager.ShowAlertTones = widgetSelectionWindow.ShowAlertTones;
GenerateChannelWidgets();
- _settingsManager.SaveSettings();
+ settingsManager.SaveSettings();
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void HandleEmergency(string dstId, string srcId)
{
bool forUs = false;
- foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
{
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
@@ -649,18 +737,28 @@ namespace DVMConsole
{
Dispatcher.Invoke(() =>
{
- _flashingManager.Start();
- _emergencyAlertPlayback.Start();
+ flashingManager.Start();
+ emergencyAlertPlayback.Start();
});
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void ChannelBox_HoldChannelButtonClicked(object sender, ChannelBox e)
{
if (e.SystemName == PLAYBACKSYS || e.ChannelName == PLAYBACKCHNAME || e.DstId == PLAYBACKTG)
return;
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void ChannelBox_PageButtonClicked(object sender, ChannelBox e)
{
if (e.SystemName == PLAYBACKSYS || e.ChannelName == PLAYBACKCHNAME || e.DstId == PLAYBACKTG)
@@ -668,18 +766,19 @@ namespace DVMConsole
Codeplug.System system = Codeplug.GetSystemForChannel(e.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(e.ChannelName);
- PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem handler = fneSystemManager.GetFneSystem(system.Name);
if (e.PageState)
- {
- handler.SendP25TDU(UInt32.Parse(system.Rid), UInt32.Parse(cpgChannel.Tgid), true);
- }
+ handler.SendP25TDU(uint.Parse(system.Rid), uint.Parse(cpgChannel.Tgid), true);
else
- {
- handler.SendP25TDU(UInt32.Parse(system.Rid), UInt32.Parse(cpgChannel.Tgid), false);
- }
+ handler.SendP25TDU(uint.Parse(system.Rid), uint.Parse(cpgChannel.Tgid), false);
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void ChannelBox_PTTButtonClicked(object sender, ChannelBox e)
{
if (e.SystemName == PLAYBACKSYS || e.ChannelName == PLAYBACKCHNAME || e.DstId == PLAYBACKTG)
@@ -687,89 +786,118 @@ namespace DVMConsole
Codeplug.System system = Codeplug.GetSystemForChannel(e.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(e.ChannelName);
- PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem handler = fneSystemManager.GetFneSystem(system.Name);
if (!e.IsSelected)
return;
FneUtils.Memset(e.mi, 0x00, P25Defines.P25_MI_LENGTH);
- uint srcId = UInt32.Parse(system.Rid);
- uint dstId = UInt32.Parse(cpgChannel.Tgid);
+ uint srcId = uint.Parse(system.Rid);
+ uint dstId = uint.Parse(cpgChannel.Tgid);
if (e.PttState)
{
e.txStreamId = handler.NewStreamId();
-
handler.SendP25TDU(srcId, dstId, true);
}
else
- {
handler.SendP25TDU(srcId, dstId, false);
- }
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void ChannelBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!isEditMode || !(sender is UIElement element)) return;
- _draggedElement = element;
- _startPoint = e.GetPosition(ChannelsCanvas);
- _offsetX = _startPoint.X - Canvas.GetLeft(_draggedElement);
- _offsetY = _startPoint.Y - Canvas.GetTop(_draggedElement);
- _isDragging = true;
+ draggedElement = element;
+ startPoint = e.GetPosition(ChannelsCanvas);
+ offsetX = startPoint.X - Canvas.GetLeft(draggedElement);
+ offsetY = startPoint.Y - Canvas.GetTop(draggedElement);
+ isDragging = true;
element.CaptureMouse();
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void ChannelBox_MouseMove(object sender, MouseEventArgs e)
{
- if (!isEditMode || !_isDragging || _draggedElement == null) return;
+ if (!isEditMode || !isDragging || draggedElement == null)
+ return;
Point currentPosition = e.GetPosition(ChannelsCanvas);
// Calculate the new position with snapping to the grid
- double newLeft = Math.Round((currentPosition.X - _offsetX) / GridSize) * GridSize;
- double newTop = Math.Round((currentPosition.Y - _offsetY) / GridSize) * GridSize;
+ double newLeft = Math.Round((currentPosition.X - offsetX) / GridSize) * GridSize;
+ double newTop = Math.Round((currentPosition.Y - offsetY) / GridSize) * GridSize;
// Ensure the box stays within canvas bounds
- newLeft = Math.Max(0, Math.Min(newLeft, ChannelsCanvas.ActualWidth - _draggedElement.RenderSize.Width));
- newTop = Math.Max(0, Math.Min(newTop, ChannelsCanvas.ActualHeight - _draggedElement.RenderSize.Height));
+ newLeft = Math.Max(0, Math.Min(newLeft, ChannelsCanvas.ActualWidth - draggedElement.RenderSize.Width));
+ newTop = Math.Max(0, Math.Min(newTop, ChannelsCanvas.ActualHeight - draggedElement.RenderSize.Height));
// Apply snapped position
- Canvas.SetLeft(_draggedElement, newLeft);
- Canvas.SetTop(_draggedElement, newTop);
+ Canvas.SetLeft(draggedElement, newLeft);
+ Canvas.SetTop(draggedElement, newTop);
// Save the new position if it's a ChannelBox
- if (_draggedElement is ChannelBox channelBox)
- {
- _settingsManager.UpdateChannelPosition(channelBox.ChannelName, newLeft, newTop);
- }
+ if (draggedElement is ChannelBox channelBox)
+ settingsManager.UpdateChannelPosition(channelBox.ChannelName, newLeft, newTop);
AdjustCanvasHeight();
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void ChannelBox_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
- if (!isEditMode || !_isDragging || _draggedElement == null) return;
+ if (!isEditMode || !isDragging || draggedElement == null)
+ return;
- _isDragging = false;
- _draggedElement.ReleaseMouseCapture();
- _draggedElement = null;
+ isDragging = false;
+ draggedElement.ReleaseMouseCapture();
+ draggedElement = null;
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void SystemStatusBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) => ChannelBox_MouseLeftButtonDown(sender, e);
+
+ ///
+ ///
+ ///
+ ///
+ ///
private void SystemStatusBox_MouseMove(object sender, MouseEventArgs e) => ChannelBox_MouseMove(sender, e);
+ ///
+ ///
+ ///
+ ///
+ ///
private void SystemStatusBox_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
- if (!isEditMode) return;
+ if (!isEditMode)
+ return;
if (sender is SystemStatusBox systemStatusBox)
{
double x = Canvas.GetLeft(systemStatusBox);
double y = Canvas.GetTop(systemStatusBox);
- _settingsManager.SystemStatusPositions[systemStatusBox.SystemName] = new ChannelPosition { X = x, Y = y };
+ settingsManager.SystemStatusPositions[systemStatusBox.SystemName] = new ChannelPosition { X = x, Y = y };
ChannelBox_MouseRightButtonDown(sender, e);
@@ -777,6 +905,11 @@ namespace DVMConsole
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void ToggleEditMode_Click(object sender, RoutedEventArgs e)
{
isEditMode = !isEditMode;
@@ -785,22 +918,26 @@ namespace DVMConsole
UpdateEditModeForWidgets();
}
+ ///
+ ///
+ ///
private void UpdateEditModeForWidgets()
{
foreach (var child in ChannelsCanvas.Children)
{
if (child is AlertTone alertTone)
- {
alertTone.IsEditMode = isEditMode;
- }
if (child is ChannelBox channelBox)
- {
channelBox.IsEditMode = isEditMode;
- }
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void AddAlertTone_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog
@@ -812,14 +949,11 @@ namespace DVMConsole
if (openFileDialog.ShowDialog() == true)
{
string alertFilePath = openFileDialog.FileName;
- var alertTone = new AlertTone(alertFilePath)
- {
- IsEditMode = isEditMode
- };
+ var alertTone = new AlertTone(alertFilePath) { IsEditMode = isEditMode };
alertTone.OnAlertTone += SendAlertTone;
- if (_settingsManager.AlertTonePositions.TryGetValue(alertFilePath, out var position))
+ if (settingsManager.AlertTonePositions.TryGetValue(alertFilePath, out var position))
{
Canvas.SetLeft(alertTone, position.X);
Canvas.SetTop(alertTone, position.Y);
@@ -833,12 +967,17 @@ namespace DVMConsole
alertTone.MouseRightButtonUp += AlertTone_MouseRightButtonUp;
ChannelsCanvas.Children.Add(alertTone);
- _settingsManager.UpdateAlertTonePaths(alertFilePath);
+ settingsManager.UpdateAlertTonePaths(alertFilePath);
AdjustCanvasHeight();
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void AlertTone_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
if (!isEditMode) return;
@@ -847,12 +986,15 @@ namespace DVMConsole
{
double x = Canvas.GetLeft(alertTone);
double y = Canvas.GetTop(alertTone);
- _settingsManager.UpdateAlertTonePosition(alertTone.AlertFilePath, x, y);
+ settingsManager.UpdateAlertTonePosition(alertTone.AlertFilePath, x, y);
AdjustCanvasHeight();
}
}
+ ///
+ ///
+ ///
private void AdjustCanvasHeight()
{
double maxBottom = 0;
@@ -861,40 +1003,44 @@ namespace DVMConsole
{
double childBottom = Canvas.GetTop(child) + child.RenderSize.Height;
if (childBottom > maxBottom)
- {
maxBottom = childBottom;
- }
}
ChannelsCanvas.Height = maxBottom + 150;
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
- if (!string.IsNullOrEmpty(_settingsManager.LastCodeplugPath) && File.Exists(_settingsManager.LastCodeplugPath))
- {
- LoadCodeplug(_settingsManager.LastCodeplugPath);
- }
+ if (!string.IsNullOrEmpty(settingsManager.LastCodeplugPath) && File.Exists(settingsManager.LastCodeplugPath))
+ LoadCodeplug(settingsManager.LastCodeplugPath);
else
- {
GenerateChannelWidgets();
- }
}
+ ///
+ ///
+ ///
+ ///
+ ///
private async void OnHoldTimerElapsed(object sender, ElapsedEventArgs e)
{
- foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
{
if (channel.SystemName == PLAYBACKSYS || channel.ChannelName == PLAYBACKCHNAME || channel.DstId == PLAYBACKTG)
continue;
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
- PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem handler = fneSystemManager.GetFneSystem(system.Name);
if (channel.HoldState && !channel.IsReceiving && !channel.PttState && !channel.PageState)
{
- handler.SendP25TDU(UInt32.Parse(system.Rid), UInt32.Parse(cpgChannel.Tgid), true);
+ handler.SendP25TDU(uint.Parse(system.Rid), uint.Parse(cpgChannel.Tgid), true);
await Task.Delay(1000);
SendAlertTone("hold.wav", true);
@@ -902,24 +1048,36 @@ namespace DVMConsole
}
}
+ ///
+ ///
+ ///
+ ///
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
- _settingsManager.SaveSettings();
+ settingsManager.SaveSettings();
base.OnClosing(e);
Application.Current.Shutdown();
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void ClearEmergency_Click(object sender, RoutedEventArgs e)
{
- _emergencyAlertPlayback.Stop();
- _flashingManager.Stop();
+ emergencyAlertPlayback.Stop();
+ flashingManager.Stop();
- foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
- {
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
channel.Emergency = false;
- }
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void btnAlert1_Click(object sender, RoutedEventArgs e)
{
Dispatcher.Invoke(() => {
@@ -927,6 +1085,11 @@ namespace DVMConsole
});
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void btnAlert2_Click(object sender, RoutedEventArgs e)
{
Dispatcher.Invoke(() =>
@@ -935,6 +1098,11 @@ namespace DVMConsole
});
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void btnAlert3_Click(object sender, RoutedEventArgs e)
{
Dispatcher.Invoke(() =>
@@ -943,6 +1111,11 @@ namespace DVMConsole
});
}
+ ///
+ ///
+ ///
+ ///
+ ///
private async void btnGlobalPtt_Click(object sender, RoutedEventArgs e)
{
if (globalPttState)
@@ -950,14 +1123,14 @@ namespace DVMConsole
globalPttState = !globalPttState;
- foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
{
if (channel.SystemName == PLAYBACKSYS || channel.ChannelName == PLAYBACKCHNAME || channel.DstId == PLAYBACKTG)
continue;
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
- PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem handler = fneSystemManager.GetFneSystem(system.Name);
channel.txStreamId = handler.NewStreamId();
@@ -969,7 +1142,7 @@ namespace DVMConsole
channel.PttState = true;
});
- handler.SendP25TDU(UInt32.Parse(system.Rid), UInt32.Parse(cpgChannel.Tgid), true);
+ handler.SendP25TDU(uint.Parse(system.Rid), uint.Parse(cpgChannel.Tgid), true);
}
else
{
@@ -979,11 +1152,16 @@ namespace DVMConsole
channel.PttState = false;
});
- handler.SendP25TDU(UInt32.Parse(system.Rid), UInt32.Parse(cpgChannel.Tgid), false);
+ handler.SendP25TDU(uint.Parse(system.Rid), uint.Parse(cpgChannel.Tgid), false);
}
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void SelectAll_Click(object sender, RoutedEventArgs e)
{
foreach (ChannelBox channel in ChannelsCanvas.Children.OfType())
@@ -1001,13 +1179,9 @@ namespace DVMConsole
channel.Background = channel.IsSelected ? (Brush)new BrushConverter().ConvertFrom("#FF0B004B") : Brushes.Gray;
if (channel.IsSelected)
- {
- _selectedChannelsManager.AddSelectedChannel(channel);
- }
+ selectedChannelsManager.AddSelectedChannel(channel);
else
- {
- _selectedChannelsManager.RemoveSelectedChannel(channel);
- }
+ selectedChannelsManager.RemoveSelectedChannel(channel);
}
}
}
@@ -1065,6 +1239,7 @@ namespace DVMConsole
{
tone = channel.toneDetector.Detect(signal);
}
+
if (tone > 0)
{
MBEToneGenerator.IMBEEncodeSingleTone((ushort)tone, imbe);
@@ -1101,17 +1276,17 @@ namespace DVMConsole
}
}
- channel.crypter.Prepare(cpgChannel.GetAlgoId(), cpgChannel.GetKeyId(), ProtocolType.P25Phase1, channel.mi);
+ channel.crypter.Prepare(cpgChannel.GetAlgoId(), cpgChannel.GetKeyId(), channel.mi);
}
// crypto time
- channel.crypter.Process(imbe, channel.p25N < 9U ? P25Crypto.FrameType.LDU1 : P25Crypto.FrameType.LDU2, 0);
+ channel.crypter.Process(imbe, channel.p25N < 9U ? P25DUID.LDU1 : P25DUID.LDU2);
// last block of LDU2, prepare a new MI
if (channel.p25N == 17U)
{
P25Crypto.CycleP25Lfsr(channel.mi);
- channel.crypter.Prepare(cpgChannel.GetAlgoId(), cpgChannel.GetKeyId(), ProtocolType.P25Phase1, channel.mi);
+ channel.crypter.Prepare(cpgChannel.GetAlgoId(), cpgChannel.GetKeyId(), channel.mi);
}
}
@@ -1177,8 +1352,8 @@ namespace DVMConsole
break;
}
- uint srcId = UInt32.Parse(system.Rid);
- uint dstId = UInt32.Parse(cpgChannel.Tgid);
+ uint srcId = uint.Parse(system.Rid);
+ uint dstId = uint.Parse(cpgChannel.Tgid);
FnePeer peer = handler.peer;
RemoteCallData callData = new RemoteCallData()
@@ -1219,7 +1394,7 @@ namespace DVMConsole
byte[] payload = new byte[200];
handler.CreateNewP25MessageHdr((byte)P25DUID.LDU2, callData, ref payload, cpgChannel.GetAlgoId(), cpgChannel.GetKeyId(), channel.mi);
- handler.CreateP25LDU2Message(channel.netLDU2, ref payload, new CryptoParams { AlgId = cpgChannel.GetAlgoId(), KeyId = cpgChannel.GetKeyId(), Mi = channel.mi });
+ handler.CreateP25LDU2Message(channel.netLDU2, ref payload, new CryptoParams { AlgId = cpgChannel.GetAlgoId(), KeyId = cpgChannel.GetKeyId(), MI = channel.mi });
peer.SendMaster(new Tuple(Constants.NET_FUNC_PROTOCOL, Constants.NET_PROTOCOL_SUBFUNC_P25), payload, pktSeq, channel.txStreamId);
}
@@ -1233,7 +1408,7 @@ namespace DVMConsole
///
///
///
- private void P25DecodeAudioFrame(byte[] ldu, P25DataReceivedEvent e, PeerSystem system, ChannelBox channel, bool emergency = false, P25Crypto.FrameType frameType = P25Crypto.FrameType.LDU1)
+ private void P25DecodeAudioFrame(byte[] ldu, P25DataReceivedEvent e, PeerSystem system, ChannelBox channel, bool emergency = false, P25DUID duid = P25DUID.LDU1)
{
try
{
@@ -1276,7 +1451,7 @@ namespace DVMConsole
short[] samples = new short[FneSystemBase.MBE_SAMPLES_LENGTH];
- channel.crypter.Process(imbe, frameType, n);
+ channel.crypter.Process(imbe, duid);
#if WIN32
if (channel.extFullRateVocoder == null)
@@ -1311,7 +1486,7 @@ namespace DVMConsole
pcmIdx += 2;
}
- _audioManager.AddTalkgroupStream(e.DstId.ToString(), pcmData);
+ audioManager.AddTalkgroupStream(e.DstId.ToString(), pcmData);
}
}
}
@@ -1345,11 +1520,11 @@ namespace DVMConsole
Dispatcher.Invoke(() =>
{
- foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
{
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
- PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem handler = fneSystemManager.GetFneSystem(system.Name);
if (cpgChannel.GetKeyId() != 0 && cpgChannel.GetAlgoId() != 0)
channel.crypter.AddKey(key.KeyId, e.KmmKey.KeysetItem.AlgId, key.GetKey());
@@ -1358,6 +1533,11 @@ namespace DVMConsole
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void KeyStatus_Click(object sender, RoutedEventArgs e)
{
KeyStatusWindow keyStatus = new KeyStatusWindow(Codeplug, this);
@@ -1382,7 +1562,7 @@ namespace DVMConsole
Dispatcher.Invoke(() =>
{
- foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
{
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
@@ -1390,7 +1570,7 @@ namespace DVMConsole
bool isEmergency = false;
bool encrypted = false;
- PeerSystem handler = _fneSystemManager.GetFneSystem(system.Name);
+ PeerSystem handler = fneSystemManager.GetFneSystem(system.Name);
if (!channel.IsEnabled)
continue;
@@ -1399,14 +1579,10 @@ namespace DVMConsole
continue;
if (!systemStatuses.ContainsKey(cpgChannel.Name))
- {
systemStatuses[cpgChannel.Name] = new SlotStatus();
- }
if (channel.decoder == null)
- {
channel.decoder = new MBEDecoder(MBE_MODE.IMBE_88BIT);
- }
SlotStatus slot = systemStatuses[cpgChannel.Name];
@@ -1422,7 +1598,7 @@ namespace DVMConsole
channel.kId = (ushort)((e.Data[182] << 8) | e.Data[183]);
Array.Copy(e.Data, 184, channel.mi, 0, P25Defines.P25_MI_LENGTH);
- channel.crypter.Prepare(channel.algId, channel.kId, P25Crypto.ProtocolType.P25Phase1, channel.mi);
+ channel.crypter.Prepare(channel.algId, channel.kId, channel.mi);
encrypted = true;
}
@@ -1593,14 +1769,14 @@ namespace DVMConsole
Array.Copy(newMI, channel.mi, P25Defines.P25_MI_LENGTH);
// decode 9 IMBE codewords into PCM samples
- P25DecodeAudioFrame(channel.netLDU2, e, handler, channel, isEmergency, P25Crypto.FrameType.LDU2);
+ P25DecodeAudioFrame(channel.netLDU2, e, handler, channel, isEmergency, P25DUID.LDU2);
}
}
break;
}
if (channel.mi != null)
- channel.crypter.Prepare(channel.algId, channel.kId, P25Crypto.ProtocolType.P25Phase1, channel.mi);
+ channel.crypter.Prepare(channel.algId, channel.kId, channel.mi);
slot.RxRFS = e.SrcId;
slot.RxType = e.FrameType;
@@ -1612,9 +1788,14 @@ namespace DVMConsole
});
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void CallHist_Click(object sender, RoutedEventArgs e)
{
callHistoryWindow.Show();
}
- }
-}
+ } // public partial class MainWindow : Window
+} // namespace dvmconsole
diff --git a/DVMConsole/P25Crypto.cs b/DVMConsole/P25Crypto.cs
index 7cf41ff..c1b7b1f 100644
--- a/DVMConsole/P25Crypto.cs
+++ b/DVMConsole/P25Crypto.cs
@@ -1,51 +1,104 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Caleb, K4PHP
*
*/
-// TODO: Move to fnecore
-
-using System;
-using System.Collections.Generic;
using System.Security.Cryptography;
-using System.Linq;
-namespace DVMConsole
+using fnecore.P25;
+
+namespace dvmconsole
{
+ ///
+ ///
+ ///
public class P25Crypto
{
- private ProtocolType protocol;
+ public const int IMBE_BUF_LEN = 11;
+
private byte algId;
private ushort keyId;
+
private byte[] messageIndicator = new byte[9];
+
private Dictionary keys = new Dictionary();
+
private byte[] aesKeystream = new byte[240]; // AES buffer
private byte[] adpKeystream = new byte[469]; // ADP buffer
- private int aesPosition;
- private int adpPosition;
+ private int ksPosition;
+
+ /*
+ ** Class
+ */
+
+ ///
+ ///
+ ///
+ private class KeyInfo
+ {
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
+ public byte AlgId { get; }
+ ///
+ ///
+ ///
+ public byte[] Key { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ public KeyInfo(byte algid, byte[] key)
+ {
+ AlgId = algid;
+ Key = key;
+ }
+ } // private class KeyInfo
+
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
public P25Crypto()
{
- this.protocol = ProtocolType.Unknown;
- this.algId = 0x80;
+ this.algId = P25Defines.P25_ALGO_UNENCRYPT;
this.keyId = 0;
- this.aesPosition = 0;
- this.adpPosition = 0;
+
+ this.ksPosition = 0;
}
+ ///
+ ///
+ ///
public void Reset()
{
keys.Clear();
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public void AddKey(ushort keyid, byte algid, byte[] key)
{
if (keyid == 0 || algid == 0x80)
@@ -54,58 +107,90 @@ namespace DVMConsole
keys[keyid] = new KeyInfo(algid, key);
}
+ ///
+ ///
+ ///
+ ///
+ ///
public bool HasKey(ushort keyId)
{
return keys.ContainsKey(keyId);
}
- public bool Prepare(byte algid, ushort keyid, ProtocolType protocol, byte[] MI)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public bool Prepare(byte algid, ushort keyid, byte[] MI)
{
this.algId = algid;
this.keyId = keyid;
- this.protocol = protocol;
+
Array.Copy(MI, this.messageIndicator, Math.Min(MI.Length, this.messageIndicator.Length));
if (!keys.ContainsKey(keyid))
return false;
- if (algid == 0x84) // AES-256
+ this.ksPosition = 0;
+
+ if (algid == P25Defines.P25_ALGO_AES)
{
- this.aesPosition = 0;
- GenerateAesKeystream();
+ GenerateAESKeystream();
return true;
}
- else if (algid == 0xAA) // ADP (RC4)
+ else if (algid == P25Defines.P25_ALGO_ARC4)
{
- this.adpPosition = 0;
- GenerateAdpKeystream();
+ GenerateARC4Keystream();
return true;
}
return false;
}
- public bool Process(byte[] PCW, FrameType frameType, int voiceSubframe)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public bool Process(byte[] imbe, P25DUID duid)
{
if (!keys.ContainsKey(keyId))
return false;
return algId switch
{
- 0x84 => AesProcess(PCW, frameType, voiceSubframe),
- 0xAA => AdpProcess(PCW, frameType, voiceSubframe),
+ P25Defines.P25_ALGO_AES => AESProcess(imbe, duid),
+ P25Defines.P25_ALGO_ARC4 => ARC4Process(imbe, duid),
_ => false
};
}
///
- /// Create ADP key stream
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void Swap(byte[] a, int i1, int i2)
+ {
+ byte temp = a[i1];
+ a[i1] = a[i2];
+ a[i2] = temp;
+ }
+
+ ///
+ /// Create ARC4 keystream.
///
- private void GenerateAdpKeystream()
+ private void GenerateARC4Keystream()
{
byte[] adpKey = new byte[13];
- byte[] S = new byte[256];
- byte[] K = new byte[256];
+ byte[] permutation = new byte[256];
+ byte[] key = new byte[256];
if (!keys.ContainsKey(keyId))
return;
@@ -127,141 +212,46 @@ namespace DVMConsole
adpKey[i] = messageIndicator[i - 5];
}
+ // generate ARC4 keystream
+ // initialize state variable
for (i = 0; i < 256; ++i)
{
- K[i] = adpKey[i % 13];
- S[i] = (byte)i;
+ key[i] = adpKey[i % 13];
+ permutation[i] = (byte)i;
}
+ // randomize, using key
for (i = 0; i < 256; ++i)
{
- j = (j + S[i] + K[i]) & 0xFF;
- Swap(S, i, j);
+ j = (j + permutation[i] + key[i]) & 0xFF;
+ Swap(permutation, i, j);
}
+ // perform RC4 transformation
i = j = 0;
for (k = 0; k < 469; ++k)
{
i = (i + 1) & 0xFF;
- j = (j + S[i]) & 0xFF;
- Swap(S, i, j);
- adpKeystream[k] = S[(S[i] + S[j]) & 0xFF];
- }
- }
-
- ///
- /// Preform a swap
- ///
- ///
- ///
- ///
- private void Swap(byte[] S, int i, int j)
- {
- byte temp = S[i];
- S[i] = S[j];
- S[j] = temp;
- }
-
- ///
- /// Process AES256
- ///
- ///
- ///
- ///
- ///
- private bool AesProcess(byte[] PCW, FrameType frameType, int voiceSubframe)
- {
- int offset = 16;
-
- switch (frameType)
- {
- case FrameType.LDU1: offset += 0; break;
- case FrameType.LDU2: offset += 101; break;
- case FrameType.V4_0: offset += 7 * voiceSubframe; break;
- case FrameType.V4_1: offset += 7 * (voiceSubframe + 4); break;
- case FrameType.V4_2: offset += 7 * (voiceSubframe + 8); break;
- case FrameType.V4_3: offset += 7 * (voiceSubframe + 12); break;
- case FrameType.V2: offset += 7 * (voiceSubframe + 16); break;
- default: return false;
- }
-
- if (protocol == ProtocolType.P25Phase1)
- {
- offset += (aesPosition * 11) + 11 + (aesPosition < 8 ? 0 : 2);
- aesPosition = (aesPosition + 1) % 9;
-
- for (int j = 0; j < 11; ++j)
- {
- PCW[j] ^= aesKeystream[j + offset];
- }
- }
- else if (protocol == ProtocolType.P25Phase2)
- {
- for (int j = 0; j < 7; ++j)
- {
- PCW[j] ^= aesKeystream[j + offset];
- }
- PCW[6] &= 0x80;
- }
-
- return true;
- }
-
- ///
- /// Process ADP
- ///
- ///
- ///
- ///
- ///
- private bool AdpProcess(byte[] PCW, FrameType frameType, int voiceSubframe)
- {
- int offset = 256;
+ j = (j + permutation[i]) & 0xFF;
- switch (frameType)
- {
- case FrameType.LDU1: offset = 0; break;
- case FrameType.LDU2: offset = 101; break;
- case FrameType.V4_0: offset += 7 * voiceSubframe; break;
- case FrameType.V4_1: offset += 7 * (voiceSubframe + 4); break;
- case FrameType.V4_2: offset += 7 * (voiceSubframe + 8); break;
- case FrameType.V4_3: offset += 7 * (voiceSubframe + 12); break;
- case FrameType.V2: offset += 7 * (voiceSubframe + 16); break;
- default: return false;
- }
+ // swap permutation[i] and permutation[j]
+ Swap(permutation, i, j);
- if (protocol == ProtocolType.P25Phase1)
- {
- offset += (adpPosition * 11) + 267 + (adpPosition < 8 ? 0 : 2);
- adpPosition = (adpPosition + 1) % 9;
-
- for (int j = 0; j < 11; ++j)
- {
- PCW[j] ^= adpKeystream[j + offset];
- }
+ // transform byte
+ adpKeystream[k] = permutation[(permutation[i] + permutation[j]) & 0xFF];
}
- else if (protocol == ProtocolType.P25Phase2)
- {
- for (int j = 0; j < 7; ++j)
- {
- PCW[j] ^= adpKeystream[j + offset];
- }
- PCW[6] &= 0x80;
- }
-
- return true;
}
///
- /// Create AES key stream
+ /// Create AES keystream.
///
- private void GenerateAesKeystream()
+ private void GenerateAESKeystream()
{
if (!keys.ContainsKey(keyId))
return;
byte[] key = keys[keyId].Key;
- byte[] iv = ExpandMiTo128(messageIndicator);
+ byte[] iv = ExpandMIToIV(messageIndicator);
using (var aes = Aes.Create())
{
@@ -287,6 +277,48 @@ namespace DVMConsole
}
}
+ ///
+ /// Helper to process IMBE audio using AES-256.
+ ///
+ ///
+ ///
+ ///
+ private bool AESProcess(byte[] imbe, P25DUID duid)
+ {
+ int offset = 16;
+ if (duid == P25DUID.LDU2)
+ offset += 101;
+
+ offset += (ksPosition * IMBE_BUF_LEN) + IMBE_BUF_LEN + (ksPosition < 8 ? 0 : 2);
+ ksPosition = (ksPosition + 1) % 9;
+
+ for (int j = 0; j < IMBE_BUF_LEN; ++j)
+ imbe[j] ^= aesKeystream[j + offset];
+
+ return true;
+ }
+
+ ///
+ /// Helper to process IMBE audio using ARC4.
+ ///
+ ///
+ ///
+ ///
+ private bool ARC4Process(byte[] imbe, P25DUID duid)
+ {
+ int offset = 256;
+ if (duid != P25DUID.LDU2)
+ offset += 101;
+
+ offset += (ksPosition * IMBE_BUF_LEN) + 267 + (ksPosition < 8 ? 0 : 2);
+ ksPosition = (ksPosition + 1) % 9;
+
+ for (int j = 0; j < IMBE_BUF_LEN; ++j)
+ imbe[j] ^= adpKeystream[j + offset];
+
+ return true;
+ }
+
///
/// Cycle P25 LFSR
///
@@ -350,7 +382,7 @@ namespace DVMConsole
///
///
///
- private static byte[] ExpandMiTo128(byte[] mi)
+ private static byte[] ExpandMIToIV(byte[] mi)
{
if (mi == null || mi.Length < 8)
throw new ArgumentException("MI must be at least 8 bytes long.");
@@ -360,16 +392,12 @@ namespace DVMConsole
// Copy first 64 bits of MI into LFSR
ulong lfsr = 0;
for (int i = 0; i < 8; i++)
- {
lfsr = (lfsr << 8) | mi[i];
- }
// Use LFSR routine to compute the expansion
ulong overflow = 0;
for (int i = 0; i < 64; i++)
- {
overflow = (overflow << 1) | StepP25Lfsr(ref lfsr);
- }
// Copy expansion and LFSR to IV
for (int i = 7; i >= 0; i--)
@@ -377,6 +405,7 @@ namespace DVMConsole
iv[i] = (byte)(overflow & 0xFF);
overflow >>= 8;
}
+
for (int i = 15; i >= 8; i--)
{
iv[i] = (byte)(lfsr & 0xFF);
@@ -385,37 +414,5 @@ namespace DVMConsole
return iv;
}
-
-
- private class KeyInfo
- {
- public byte AlgId { get; }
- public byte[] Key { get; }
-
- public KeyInfo(byte algid, byte[] key)
- {
- AlgId = algid;
- Key = key;
- }
- }
-
- public enum ProtocolType
- {
- Unknown = 0,
- P25Phase1,
- P25Phase2
- }
-
- public enum FrameType
- {
- Unknown = 0,
- LDU1,
- LDU2,
- V2,
- V4_0,
- V4_1,
- V4_2,
- V4_3
- }
- }
-}
+ } // public class P25Crypto
+} // namespace dvmconsole
diff --git a/DVMConsole/PeerSystem.cs b/DVMConsole/PeerSystem.cs
index dfe722f..6b2c8be 100644
--- a/DVMConsole/PeerSystem.cs
+++ b/DVMConsole/PeerSystem.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL
@@ -16,7 +16,7 @@ using System.Net;
using fnecore;
-namespace DVMConsole
+namespace dvmconsole
{
///
/// Implements a peer FNE router system.
@@ -113,4 +113,4 @@ namespace DVMConsole
/* stub */
}
} // public class PeerSystem
-} // namespace rc2_dvm
\ No newline at end of file
+} // namespace dvmconsole
diff --git a/DVMConsole/QuickCallPage.xaml b/DVMConsole/QuickCallPage.xaml
index e563b43..2c7a4a1 100644
--- a/DVMConsole/QuickCallPage.xaml
+++ b/DVMConsole/QuickCallPage.xaml
@@ -1,9 +1,9 @@
-
diff --git a/DVMConsole/QuickCallPage.xaml.cs b/DVMConsole/QuickCallPage.xaml.cs
index ff2b286..322b70f 100644
--- a/DVMConsole/QuickCallPage.xaml.cs
+++ b/DVMConsole/QuickCallPage.xaml.cs
@@ -1,26 +1,19 @@
-/*
-* WhackerLink - DVMConsole
+// SPDX-License-Identifier: AGPL-3.0-only
+/**
+* Digital Voice Modem - Desktop Dispatch Console
+* AGPLv3 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* 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 3 of the License, or
-* (at your option) any later version.
+* @package DVM / Desktop Dispatch Console
+* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
-* 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.
+* Copyright (C) 2024 Caleb, K4PHP
*
-* You should have received a copy of the GNU General Public License
-* along with this program. If not, see .
-*
-* Copyright (C) 2024 Caleb, K4PHP
-*
*/
using System.Windows;
-namespace DVMConsole
+namespace dvmconsole
{
///
/// Interaction logic for QuickCallPage.xaml
@@ -30,11 +23,23 @@ namespace DVMConsole
public string ToneA;
public string ToneB;
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
public QuickCallPage()
{
InitializeComponent();
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void SendButton_Click(object sender, RoutedEventArgs e)
{
ToneA = ToneAText.Text;
@@ -43,5 +48,5 @@ namespace DVMConsole
DialogResult = true;
Close();
}
- }
-}
+ } // public partial class QuickCallPage : Window
+} // namespace dvmconsole
diff --git a/DVMConsole/SampleTimeConvert.cs b/DVMConsole/SampleTimeConvert.cs
index 65109d1..4fe08e6 100644
--- a/DVMConsole/SampleTimeConvert.cs
+++ b/DVMConsole/SampleTimeConvert.cs
@@ -13,7 +13,7 @@
using NAudio.Wave;
-namespace DVMConsole
+namespace dvmconsole
{
///
///
@@ -67,5 +67,5 @@ namespace DVMConsole
{
return ToBytes(format, ToSamples(format, ms));
}
- } // public class SamplesToMS
-} // namespace dvmbridge
\ No newline at end of file
+ } // public class SampleTimeConvert
+} // namespace dvmconsole
diff --git a/DVMConsole/SelectedChannelsManager.cs b/DVMConsole/SelectedChannelsManager.cs
index 11203ee..4ffb0c2 100644
--- a/DVMConsole/SelectedChannelsManager.cs
+++ b/DVMConsole/SelectedChannelsManager.cs
@@ -1,59 +1,86 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2024 Caleb, K4PHP
*
*/
-using DVMConsole.Controls;
+using dvmconsole.Controls;
-namespace DVMConsole
+namespace dvmconsole
{
+ ///
+ ///
+ ///
public class SelectedChannelsManager
{
- private readonly HashSet _selectedChannels;
+ private readonly HashSet selectedChannels;
+ public IReadOnlyCollection GetSelectedChannels() => selectedChannels;
+
+ /*
+ ** Events
+ */
+
+ ///
+ ///
+ ///
public event Action SelectedChannelsChanged;
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
public SelectedChannelsManager()
{
- _selectedChannels = new HashSet();
+ selectedChannels = new HashSet();
}
+ ///
+ ///
+ ///
+ ///
public void AddSelectedChannel(ChannelBox channel)
{
- if (_selectedChannels.Add(channel))
+ if (selectedChannels.Add(channel))
{
channel.IsSelected = true;
SelectedChannelsChanged.Invoke();
}
}
+ ///
+ ///
+ ///
+ ///
public void RemoveSelectedChannel(ChannelBox channel)
{
- if (_selectedChannels.Remove(channel))
+ if (selectedChannels.Remove(channel))
{
channel.IsSelected = false;
SelectedChannelsChanged.Invoke();
}
}
+ ///
+ ///
+ ///
public void ClearSelections()
{
- foreach (var channel in _selectedChannels)
- {
+ foreach (var channel in selectedChannels)
channel.IsSelected = false;
- }
- _selectedChannels.Clear();
+
+ selectedChannels.Clear();
SelectedChannelsChanged.Invoke();
}
-
- public IReadOnlyCollection GetSelectedChannels() => _selectedChannels;
- }
-}
+ } // public class SelectedChannelsManager
+} // namespace dvmconsole
diff --git a/DVMConsole/SettingsManager.cs b/DVMConsole/SettingsManager.cs
index a7e83d6..4de5863 100644
--- a/DVMConsole/SettingsManager.cs
+++ b/DVMConsole/SettingsManager.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2024-2025 Caleb, K4PHP
@@ -12,26 +12,68 @@
*/
using System.IO;
+
using Newtonsoft.Json;
-namespace DVMConsole
+namespace dvmconsole
{
+ ///
+ ///
+ ///
public class SettingsManager
{
private const string SettingsFilePath = "UserSettings.json";
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
public bool ShowSystemStatus { get; set; } = true;
+ ///
+ ///
+ ///
public bool ShowChannels { get; set; } = true;
+ ///
+ ///
+ ///
public bool ShowAlertTones { get; set; } = true;
+ ///
+ ///
+ ///
public string LastCodeplugPath { get; set; } = null;
+ ///
+ ///
+ ///
public Dictionary ChannelPositions { get; set; } = new Dictionary();
+ ///
+ ///
+ ///
public Dictionary SystemStatusPositions { get; set; } = new Dictionary();
+ ///
+ ///
+ ///
public List AlertToneFilePaths { get; set; } = new List();
+ ///
+ ///
+ ///
public Dictionary AlertTonePositions { get; set; } = new Dictionary();
+ ///
+ ///
+ ///
public Dictionary ChannelOutputDevices { get; set; } = new Dictionary();
+ /*
+ ** Methods
+ */
+
+ ///
+ ///
+ ///
public void LoadSettings()
{
if (!File.Exists(SettingsFilePath)) return;
@@ -60,6 +102,10 @@ namespace DVMConsole
}
}
+ ///
+ ///
+ ///
+ ///
public void UpdateAlertTonePaths(string newFilePath)
{
if (!AlertToneFilePaths.Contains(newFilePath))
@@ -69,30 +115,56 @@ namespace DVMConsole
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public void UpdateAlertTonePosition(string alertFileName, double x, double y)
{
AlertTonePositions[alertFileName] = new ChannelPosition { X = x, Y = y };
SaveSettings();
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public void UpdateChannelPosition(string channelName, double x, double y)
{
ChannelPositions[channelName] = new ChannelPosition { X = x, Y = y };
SaveSettings();
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public void UpdateSystemStatusPosition(string systemName, double x, double y)
{
SystemStatusPositions[systemName] = new ChannelPosition { X = x, Y = y };
SaveSettings();
}
+ ///
+ ///
+ ///
+ ///
+ ///
public void UpdateChannelOutputDevice(string channelName, int deviceIndex)
{
ChannelOutputDevices[channelName] = deviceIndex;
SaveSettings();
}
+ ///
+ ///
+ ///
public void SaveSettings()
{
try
@@ -105,5 +177,5 @@ namespace DVMConsole
Console.WriteLine($"Error saving settings: {ex.Message}");
}
}
- }
-}
+ } // public class SettingsManager
+} // namespace dvmconsole
diff --git a/DVMConsole/ToneGenerator.cs b/DVMConsole/ToneGenerator.cs
index 8b49fbc..6068fc7 100644
--- a/DVMConsole/ToneGenerator.cs
+++ b/DVMConsole/ToneGenerator.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2024-2025 Caleb, K4PHP
@@ -13,27 +13,31 @@
using NAudio.Wave;
-namespace DVMConsole
+namespace dvmconsole
{
///
///
///
public class ToneGenerator
{
- private readonly int _sampleRate = 8000;
- private readonly int _bitsPerSample = 16;
- private readonly int _channels = 1;
- private WaveOutEvent _waveOut;
- private BufferedWaveProvider _waveProvider;
+ private readonly int sampleRate = 8000;
+ private readonly int bitsPerSample = 16;
+ private readonly int channels = 1;
+ private WaveOutEvent waveOut;
+ private BufferedWaveProvider waveProvider;
+
+ /*
+ ** Methods
+ */
///
- /// Creates an instance of
+ /// Initializes a new instance of the class.
///
public ToneGenerator()
{
- _waveOut = new WaveOutEvent();
- _waveProvider = new BufferedWaveProvider(new WaveFormat(_sampleRate, _bitsPerSample, _channels));
- _waveOut.Init(_waveProvider);
+ waveOut = new WaveOutEvent();
+ waveProvider = new BufferedWaveProvider(new WaveFormat(sampleRate, bitsPerSample, channels));
+ waveOut.Init(waveProvider);
}
///
@@ -44,12 +48,12 @@ namespace DVMConsole
/// PCM data as a byte array
public byte[] GenerateTone(double frequency, double durationSeconds)
{
- int sampleCount = (int)(_sampleRate * durationSeconds);
- byte[] buffer = new byte[sampleCount * (_bitsPerSample / 8)];
+ int sampleCount = (int)(sampleRate * durationSeconds);
+ byte[] buffer = new byte[sampleCount * (bitsPerSample / 8)];
for (int i = 0; i < sampleCount; i++)
{
- double time = (double)i / _sampleRate;
+ double time = (double)i / sampleRate;
short sampleValue = (short)(Math.Sin(2 * Math.PI * frequency * time) * short.MaxValue);
buffer[i * 2] = (byte)(sampleValue & 0xFF);
@@ -68,10 +72,10 @@ namespace DVMConsole
{
byte[] toneData = GenerateTone(frequency, durationSeconds);
- _waveProvider.ClearBuffer();
- _waveProvider.AddSamples(toneData, 0, toneData.Length);
+ waveProvider.ClearBuffer();
+ waveProvider.AddSamples(toneData, 0, toneData.Length);
- _waveOut.Play();
+ waveOut.Play();
}
///
@@ -79,7 +83,7 @@ namespace DVMConsole
///
public void StopTone()
{
- _waveOut.Stop();
+ waveOut.Stop();
}
///
@@ -87,7 +91,7 @@ namespace DVMConsole
///
public void Dispose()
{
- _waveOut.Dispose();
+ waveOut.Dispose();
}
- }
-}
+ } // public class ToneGenerator
+} // namespace dvmconsole
diff --git a/DVMConsole/VocoderInterop.cs b/DVMConsole/VocoderInterop.cs
index 57bd0f9..4b6181a 100644
--- a/DVMConsole/VocoderInterop.cs
+++ b/DVMConsole/VocoderInterop.cs
@@ -1,28 +1,58 @@
-// From https://github.com/w3axl/rc2-dvm
+// SPDX-License-Identifier: AGPL-3.0-only
+/**
+* Digital Voice Modem - Desktop Dispatch Console
+* AGPLv3 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Desktop Dispatch Console
+* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
+*
+* Copyright (C) 2025 Patrick McDonnell, W3AXL
+*
+*/
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
-using fnecore;
-namespace DVMConsole
+namespace dvmconsole
{
+ ///
+ ///
+ ///
public enum MBE_MODE
{
DMR_AMBE, //! DMR AMBE
IMBE_88BIT, //! 88-bit IMBE (P25)
- }
+ } // public enum MBE_MODE
///
- /// Wrapper class for the c++ dvmvocoder encoder library
+ /// Wrapper class for the C++ dvmvocoder encoder library.
///
/// Using info from https://stackoverflow.com/a/315064/1842613
public class MBEEncoder
{
+ private IntPtr encoder;
+
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Vocoder Mode (DMR or P25)
+ public MBEEncoder(MBE_MODE mode)
+ {
+ encoder = MBEEncoder_Create(mode);
+ }
+
+ ///
+ /// Finalizes a instance of the class.
+ ///
+ ~MBEEncoder()
+ {
+ MBEEncoder_Delete(encoder);
+ }
+
///
/// Create a new MBEEncoder
///
@@ -54,28 +84,6 @@ namespace DVMConsole
[DllImport("libvocoder", CallingConvention = CallingConvention.Cdecl)]
public static extern void MBEEncoder_Delete(IntPtr pEncoder);
- ///
- /// Pointer to the encoder instance
- ///
- private IntPtr encoder { get; set; }
-
- ///
- /// Create a new MBEEncoder instance
- ///
- /// Vocoder Mode (DMR or P25)
- public MBEEncoder(MBE_MODE mode)
- {
- encoder = MBEEncoder_Create(mode);
- }
-
- ///
- /// Private class destructor properly deletes interop instance
- ///
- ~MBEEncoder()
- {
- MBEEncoder_Delete(encoder);
- }
-
///
/// Encode PCM16 samples to MBE codeword
///
@@ -86,17 +94,45 @@ namespace DVMConsole
MBEEncoder_Encode(encoder, samples, codeword);
}
+ ///
+ ///
+ ///
+ ///
+ ///
public void encodeBits([In] char[] bits, [Out] byte[] codeword)
{
MBEEncoder_EncodeBits(encoder, bits, codeword);
}
- }
+ } // public class MBEEncoder
///
- /// Wrapper class for the c++ dvmvocoder decoder library
+ /// Wrapper class for the C++ dvmvocoder decoder library.
///
public class MBEDecoder
{
+ private IntPtr decoder;
+
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Vocoder Mode (DMR or P25)
+ public MBEDecoder(MBE_MODE mode)
+ {
+ decoder = MBEDecoder_Create(mode);
+ }
+
+ ///
+ /// Finalizes a instance of the class.
+ ///
+ ~MBEDecoder()
+ {
+ MBEDecoder_Delete(decoder);
+ }
+
///
/// Create a new MBEDecoder
///
@@ -130,28 +166,6 @@ namespace DVMConsole
[DllImport("libvocoder", CallingConvention = CallingConvention.Cdecl)]
public static extern void MBEDecoder_Delete(IntPtr pDecoder);
- ///
- /// Pointer to the decoder instance
- ///
- private IntPtr decoder { get; set; }
-
- ///
- /// Create a new MBEDecoder instance
- ///
- /// Vocoder Mode (DMR or P25)
- public MBEDecoder(MBE_MODE mode)
- {
- decoder = MBEDecoder_Create(mode);
- }
-
- ///
- /// Private class destructor properly deletes interop instance
- ///
- ~MBEDecoder()
- {
- MBEDecoder_Delete(decoder);
- }
-
///
/// Decode MBE codeword to PCM16 samples
///
@@ -172,8 +186,11 @@ namespace DVMConsole
{
return MBEDecoder_DecodeBits(decoder, codeword, bits);
}
- }
+ } // public class MBEDecoder
+ ///
+ ///
+ ///
public static class MBEToneGenerator
{
///
@@ -183,7 +200,7 @@ namespace DVMConsole
///
///
///
- public static void AmbeEncodeSingleTone(int tone_freq_hz, char tone_amplitude, [Out] byte[] codeword)
+ public static void AMBEEncodeSingleTone(int tone_freq_hz, char tone_amplitude, [Out] byte[] codeword)
{
// U bit vectors
// u0 and u1 are 12 bits
@@ -197,15 +214,11 @@ namespace DVMConsole
// Validate tone index
if (tone_idx < 5 || tone_idx > 122)
- {
throw new ArgumentOutOfRangeException($"Tone index for frequency out of range!");
- }
// Validate amplitude value
if (tone_amplitude > 127)
- {
throw new ArgumentOutOfRangeException("Tone amplitude must be between 0 and 127!");
- }
// Make sure tone index only has 7 bits (it should but we make sure :) )
tone_idx &= 0b01111111;
@@ -258,8 +271,11 @@ namespace DVMConsole
byte[] tone_codeword = VocoderToneLookupTable.IMBEToneFrames[nearest];
Array.Copy(tone_codeword, codeword, tone_codeword.Length);
}
- }
+ } // public static class MBEToneGenerator
+ ///
+ ///
+ ///
public class MBEInterleaver
{
public const int PCM_SAMPLES = 160;
@@ -273,6 +289,14 @@ namespace DVMConsole
private MBEEncoder encoder;
private MBEDecoder decoder;
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
public MBEInterleaver(MBE_MODE mode)
{
this.mode = mode;
@@ -280,13 +304,19 @@ namespace DVMConsole
decoder = new MBEDecoder(this.mode);
}
- public Int32 Decode([In] byte[] codeword, [Out] byte[] mbeBits)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public int Decode([In] byte[] codeword, [Out] byte[] mbeBits)
{
// Input validation
if (codeword == null)
- {
throw new NullReferenceException("Input MBE codeword is null!");
- }
char[] bits = null;
@@ -294,24 +324,18 @@ namespace DVMConsole
if (mode == MBE_MODE.DMR_AMBE)
{
if (codeword.Length != AMBE_CODEWORD_SAMPLES)
- {
throw new ArgumentOutOfRangeException($"AMBE codeword length is != {AMBE_CODEWORD_SAMPLES}");
- }
bits = new char[AMBE_CODEWORD_BITS];
}
else if (mode == MBE_MODE.IMBE_88BIT)
{
if (codeword.Length != IMBE_CODEWORD_SAMPLES)
- {
throw new ArgumentOutOfRangeException($"IMBE codeword length is != {IMBE_CODEWORD_SAMPLES}");
- }
bits = new char[IMBE_CODEWORD_BITS];
}
if (bits == null)
- {
throw new NullReferenceException("Failed to initialize decoder");
- }
// Decode
int errs = decoder.decodeBits(codeword, bits);
@@ -334,12 +358,18 @@ namespace DVMConsole
return errs;
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public void Encode([In] byte[] mbeBits, [Out] byte[] codeword)
{
if (mbeBits == null)
- {
throw new NullReferenceException("Input MBE bit array is null!");
- }
char[] bits = null;
@@ -347,34 +377,30 @@ namespace DVMConsole
if (mode == MBE_MODE.DMR_AMBE)
{
if (mbeBits.Length != AMBE_CODEWORD_BITS)
- {
throw new ArgumentOutOfRangeException($"AMBE codeword bit length is != {AMBE_CODEWORD_BITS}");
- }
bits = new char[AMBE_CODEWORD_BITS];
Array.Copy(mbeBits, bits, AMBE_CODEWORD_BITS);
}
else if (mode == MBE_MODE.IMBE_88BIT)
{
if (mbeBits.Length != IMBE_CODEWORD_BITS)
- {
throw new ArgumentOutOfRangeException($"IMBE codeword bit length is != {AMBE_CODEWORD_BITS}");
- }
bits = new char[IMBE_CODEWORD_BITS];
Array.Copy(mbeBits, bits, IMBE_CODEWORD_BITS);
}
if (bits == null)
- {
throw new ArgumentException("Bit array did not get set up properly!");
- }
// Encode samples
if (mode == MBE_MODE.DMR_AMBE)
{
// Create output array
byte[] codewords = new byte[AMBE_CODEWORD_SAMPLES];
+
// Encode
encoder.encodeBits(bits, codewords);
+
// Copy
codeword = new byte[AMBE_CODEWORD_SAMPLES];
Array.Copy(codewords, codeword, IMBE_CODEWORD_SAMPLES);
@@ -383,12 +409,14 @@ namespace DVMConsole
{
// Create output array
byte[] codewords = new byte[IMBE_CODEWORD_SAMPLES];
+
// Encode
encoder.encodeBits(bits, codewords);
+
// Copy
codeword = new byte[IMBE_CODEWORD_SAMPLES];
Array.Copy(codewords, codeword, IMBE_CODEWORD_SAMPLES);
}
}
- }
-}
\ No newline at end of file
+ } // public class MBEInterleaver
+} // namespace dvmconsole
diff --git a/DVMConsole/VocoderToneLookupTable.cs b/DVMConsole/VocoderToneLookupTable.cs
index a1afcb3..bdcc0f0 100644
--- a/DVMConsole/VocoderToneLookupTable.cs
+++ b/DVMConsole/VocoderToneLookupTable.cs
@@ -1,4 +1,17 @@
-namespace DVMConsole
+// SPDX-License-Identifier: AGPL-3.0-only
+/**
+* Digital Voice Modem - Desktop Dispatch Console
+* AGPLv3 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Desktop Dispatch Console
+* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
+*
+* Copyright (C) 2025 Patrick McDonnell, W3AXL
+*
+*/
+
+namespace dvmconsole
{
///
/// From https://github.com/W3AXL/rc2-dvm/blob/main/rc2-dvm/Audio.cs
@@ -84,5 +97,5 @@
{ 2469, new byte[] { 0x5, 0x6, 0xFB, 0x63, 0xCD, 0xD9, 0x2B, 0x42, 0xE1, 0xCF, 0x6D } },
{ 2500, new byte[] { 0x5, 0x6, 0xFB, 0x63, 0xCD, 0xD9, 0x2B, 0x42, 0xE1, 0xCF, 0x6B } },
};
- }
-}
+ } // public class VocoderToneLookupTable
+} // namespace dvmconsole
diff --git a/DVMConsole/WaveFilePlaybackManager.cs b/DVMConsole/WaveFilePlaybackManager.cs
index ff11299..acdbe39 100644
--- a/DVMConsole/WaveFilePlaybackManager.cs
+++ b/DVMConsole/WaveFilePlaybackManager.cs
@@ -1,91 +1,125 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2024 Caleb, K4PHP
*
*/
-using NAudio.Wave;
using System.Windows.Threading;
-namespace DVMConsole
+using NAudio.Wave;
+
+namespace dvmconsole
{
+ ///
+ ///
+ ///
public class WaveFilePlaybackManager
{
- private readonly string _waveFilePath;
- private readonly DispatcherTimer _timer;
- private WaveOutEvent _waveOut;
- private AudioFileReader _audioFileReader;
- private bool _isPlaying;
+ private readonly string waveFilePath;
+ private readonly DispatcherTimer timer;
+ private WaveOutEvent waveOut;
+ private AudioFileReader audioFileReader;
+ private bool isPlaying;
+
+ /*
+ ** Methods
+ */
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ ///
public WaveFilePlaybackManager(string waveFilePath, int intervalMilliseconds = 500)
{
if (string.IsNullOrEmpty(waveFilePath))
throw new ArgumentNullException(nameof(waveFilePath), "Wave file path cannot be null or empty.");
- _waveFilePath = waveFilePath;
- _timer = new DispatcherTimer
+ this.waveFilePath = waveFilePath;
+ timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(intervalMilliseconds)
};
- _timer.Tick += OnTimerTick;
+ timer.Tick += OnTimerTick;
}
+ ///
+ ///
+ ///
public void Start()
{
- if (_isPlaying)
+ if (isPlaying)
return;
InitializeAudio();
- _isPlaying = true;
- _timer.Start();
+ isPlaying = true;
+ timer.Start();
}
+ ///
+ ///
+ ///
public void Stop()
{
- if (!_isPlaying)
+ if (!isPlaying)
return;
- _timer.Stop();
+ timer.Stop();
DisposeAudio();
- _isPlaying = false;
+ isPlaying = false;
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void OnTimerTick(object sender, EventArgs e)
{
PlayAudio();
}
+ ///
+ ///
+ ///
private void InitializeAudio()
{
- _audioFileReader = new AudioFileReader(_waveFilePath);
- _waveOut = new WaveOutEvent();
- _waveOut.Init(_audioFileReader);
+ audioFileReader = new AudioFileReader(waveFilePath);
+ waveOut = new WaveOutEvent();
+ waveOut.Init(audioFileReader);
}
+ ///
+ ///
+ ///
private void PlayAudio()
{
- if (_waveOut != null && _waveOut.PlaybackState != PlaybackState.Playing)
+ if (waveOut != null && waveOut.PlaybackState != PlaybackState.Playing)
{
- _waveOut.Stop();
- _audioFileReader.Position = 0;
- _waveOut.Play();
+ waveOut.Stop();
+ audioFileReader.Position = 0;
+ waveOut.Play();
}
}
+ ///
+ ///
+ ///
private void DisposeAudio()
{
- _waveOut?.Stop();
- _waveOut?.Dispose();
- _audioFileReader?.Dispose();
- _waveOut = null;
- _audioFileReader = null;
+ waveOut?.Stop();
+ waveOut?.Dispose();
+ audioFileReader?.Dispose();
+ waveOut = null;
+ audioFileReader = null;
}
- }
-}
+ } // public class WaveFilePlaybackManager
+} // namespace dvmconsole
diff --git a/DVMConsole/WidgetSelectionWindow.xaml b/DVMConsole/WidgetSelectionWindow.xaml
index 1186f7f..14174b3 100644
--- a/DVMConsole/WidgetSelectionWindow.xaml
+++ b/DVMConsole/WidgetSelectionWindow.xaml
@@ -1,4 +1,4 @@
-
diff --git a/DVMConsole/WidgetSelectionWindow.xaml.cs b/DVMConsole/WidgetSelectionWindow.xaml.cs
index 4b160f0..e09f6f4 100644
--- a/DVMConsole/WidgetSelectionWindow.xaml.cs
+++ b/DVMConsole/WidgetSelectionWindow.xaml.cs
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
/**
-* Digital Voice Modem - DVMConsole
+* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* @package DVM / DVM Console
+* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2024 Caleb, K4PHP
@@ -13,19 +13,47 @@
using System.Windows;
-namespace DVMConsole
+namespace dvmconsole
{
+ ///
+ /// Interaction logic for WidgetSelectionWindow.xaml
+ ///
public partial class WidgetSelectionWindow : Window
{
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
public bool ShowSystemStatus { get; private set; } = true;
+ ///
+ ///
+ ///
public bool ShowChannels { get; private set; } = true;
+ ///
+ ///
+ ///
public bool ShowAlertTones { get; private set; } = true;
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
public WidgetSelectionWindow()
{
InitializeComponent();
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void ApplyButton_Click(object sender, RoutedEventArgs e)
{
ShowSystemStatus = SystemStatusCheckBox.IsChecked ?? false;
@@ -34,5 +62,5 @@ namespace DVMConsole
DialogResult = true;
Close();
}
- }
-}
+ } // public partial class WidgetSelectionWindow : Window
+} // namespace dvmconsole
diff --git a/DVMConsole/codeplugs/codeplug.yml b/DVMConsole/codeplugs/codeplug.yml
index 86e3f98..66e4583 100644
--- a/DVMConsole/codeplugs/codeplug.yml
+++ b/DVMConsole/codeplugs/codeplug.yml
@@ -33,8 +33,8 @@ zones:
tgid: "2001"
# Encryption Key Id (If 0 or blank, will be assumed clear)
keyId: 0x50
- # Algorithm Id 0xAA or 0x84 (RC4 or AES) (If 0 or blank, will be assumed clear)
- algoId: 0xaa
+ # Algorithm AES ("aes"), ADP/ARC4 ("arc4"), None ("none")
+ algo: "aes"
# Ignored now, we use dvmfne KMM support (This will be used in the future to ovveride FNE KMM support)
encryptionKey: null
- name: "Channel 2"
diff --git a/README.md b/README.md
index ef1dcb1..84891ba 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,18 @@
-# DVMConsole
-### DVM Desktop Console
-
+# Digital Voice Modem Desktop Dispatch Console
+
+This provides a desktop application that mimics or otherwise operates like a system dispatch console.
+
## Setup
- Download the packaged release from the releases or clone and build yourself
- Modify the codeplug file
- Select the codeplug once opening the app
+
## Features
- Custumizable widgets
- Individual channel audio control
- AES and RC4 crypto support
-- Auto saved and transferable user settings
\ No newline at end of file
+- Auto saved and transferable user settings
+
+## License
+
+This project is licensed under the AGPLv3 License - see the [LICENSE](LICENSE) file for details. Use of this project is intended, for amateur and/or educational use ONLY. Any other use is at the risk of user and all commercial purposes is strictly discouraged.
diff --git a/DVMConsole/AlertTone.xaml b/dvmconsole/Controls/AlertTone.xaml
similarity index 92%
rename from DVMConsole/AlertTone.xaml
rename to dvmconsole/Controls/AlertTone.xaml
index a53cd09..3b683cb 100644
--- a/DVMConsole/AlertTone.xaml
+++ b/dvmconsole/Controls/AlertTone.xaml
@@ -1,4 +1,4 @@
-
-
-
+
-
-
diff --git a/DVMConsole/AlertTone.xaml.cs b/dvmconsole/Controls/AlertTone.xaml.cs
similarity index 53%
rename from DVMConsole/AlertTone.xaml.cs
rename to dvmconsole/Controls/AlertTone.xaml.cs
index c3d2101..9dd8071 100644
--- a/DVMConsole/AlertTone.xaml.cs
+++ b/dvmconsole/Controls/AlertTone.xaml.cs
@@ -1,51 +1,71 @@
-/*
-* WhackerLink - DVMConsole
+// SPDX-License-Identifier: AGPL-3.0-only
+/**
+* Digital Voice Modem - Desktop Dispatch Console
+* AGPLv3 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
-* 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 3 of the License, or
-* (at your option) any later version.
+* @package DVM / Desktop Dispatch Console
+* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
-* 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.
+* Copyright (C) 2025 Caleb, K4PHP
*
-* You should have received a copy of the GNU General Public License
-* along with this program. If not, see .
-*
-* Copyright (C) 2024 Caleb, K4PHP
-*
*/
-using System.Media;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
-namespace DVMConsole.Controls
+namespace dvmconsole.Controls
{
+ ///
+ ///
+ ///
public partial class AlertTone : UserControl
{
- public event Action OnAlertTone;
+ private Point startPoint;
+ private bool isDragging;
public static readonly DependencyProperty AlertFileNameProperty =
DependencyProperty.Register("AlertFileName", typeof(string), typeof(AlertTone), new PropertyMetadata(string.Empty));
+ /*
+ ** Properties
+ */
+
+ ///
+ ///
+ ///
public string AlertFileName
{
get => (string)GetValue(AlertFileNameProperty);
set => SetValue(AlertFileNameProperty, value);
}
+ ///
+ ///
+ ///
public string AlertFilePath { get; set; }
- private Point _startPoint;
- private bool _isDragging;
-
+ ///
+ ///
+ ///
public bool IsEditMode { get; set; }
+ /*
+ ** Events
+ */
+
+ public event Action OnAlertTone;
+
+ /*
+ ** Methods
+ */
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
public AlertTone(string alertFilePath)
{
InitializeComponent();
@@ -57,29 +77,44 @@ namespace DVMConsole.Controls
this.MouseRightButtonDown += AlertTone_MouseRightButtonDown;
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void PlayAlert_Click(object sender, RoutedEventArgs e)
{
OnAlertTone.Invoke(this);
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void AlertTone_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!IsEditMode) return;
- _startPoint = e.GetPosition(this);
- _isDragging = true;
+ startPoint = e.GetPosition(this);
+ isDragging = true;
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void AlertTone_MouseMove(object sender, MouseEventArgs e)
{
- if (_isDragging && IsEditMode)
+ if (isDragging && IsEditMode)
{
var parentCanvas = VisualTreeHelper.GetParent(this) as Canvas;
if (parentCanvas != null)
{
Point mousePos = e.GetPosition(parentCanvas);
- double newLeft = mousePos.X - _startPoint.X;
- double newTop = mousePos.Y - _startPoint.Y;
+ double newLeft = mousePos.X - startPoint.X;
+ double newTop = mousePos.Y - startPoint.Y;
Canvas.SetLeft(this, Math.Max(0, newLeft));
Canvas.SetTop(this, Math.Max(0, newTop));
@@ -87,11 +122,16 @@ namespace DVMConsole.Controls
}
}
+ ///
+ ///
+ ///
+ ///
+ ///
private void AlertTone_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
- if (!IsEditMode || !_isDragging) return;
+ if (!IsEditMode || !isDragging) return;
- _isDragging = false;
+ isDragging = false;
var parentCanvas = VisualTreeHelper.GetParent(this) as Canvas;
if (parentCanvas != null)
@@ -102,10 +142,5 @@ namespace DVMConsole.Controls
ReleaseMouseCapture();
}
-
- private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
- {
-
- }
- }
-}
+ } // public partial class AlertTone : UserControl
+} // namespace dvmconsole.Controls
diff --git a/DVMConsole/ChannelBox.xaml b/dvmconsole/Controls/ChannelBox.xaml
similarity index 95%
rename from DVMConsole/ChannelBox.xaml
rename to dvmconsole/Controls/ChannelBox.xaml
index 64cb60a..1be0e67 100644
--- a/DVMConsole/ChannelBox.xaml
+++ b/dvmconsole/Controls/ChannelBox.xaml
@@ -1,4 +1,4 @@
-
@@ -42,9 +42,9 @@
+ Height="21" VerticalAlignment="Top" x:Name="VolumeSlider"
+ ValueChanged="VolumeSlider_ValueChanged" Margin="11,10,65,0"
+ Grid.ColumnSpan="2" Grid.Row="2">