diff --git a/WhackerLinkConsoleV2.sln b/WhackerLinkConsoleV2.sln
index b14cad8..ad8965d 100644
--- a/WhackerLinkConsoleV2.sln
+++ b/WhackerLinkConsoleV2.sln
@@ -70,8 +70,8 @@ Global
{710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}.Release|Any CPU.Build.0 = Release|Any CPU
{710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}.Release|x64.ActiveCfg = Release|x64
{710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}.Release|x64.Build.0 = Release|x64
- {710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}.Release|x86.ActiveCfg = Release|Any CPU
- {710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}.Release|x86.Build.0 = Release|Any CPU
+ {710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}.Release|x86.ActiveCfg = Release|x86
+ {710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}.Release|x86.Build.0 = Release|x86
{710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}.WIN32|Any CPU.ActiveCfg = WIN32|Any CPU
{710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}.WIN32|Any CPU.Build.0 = WIN32|Any CPU
{710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}.WIN32|x64.ActiveCfg = WIN32|x64
@@ -98,10 +98,10 @@ Global
{1F06ECB1-9928-1430-63F4-2E01522A0510}.Release|x86.Build.0 = Release|Any CPU
{1F06ECB1-9928-1430-63F4-2E01522A0510}.WIN32|Any CPU.ActiveCfg = WIN32|Any CPU
{1F06ECB1-9928-1430-63F4-2E01522A0510}.WIN32|Any CPU.Build.0 = WIN32|Any CPU
- {1F06ECB1-9928-1430-63F4-2E01522A0510}.WIN32|x64.ActiveCfg = WIN32|Any CPU
- {1F06ECB1-9928-1430-63F4-2E01522A0510}.WIN32|x64.Build.0 = WIN32|Any CPU
- {1F06ECB1-9928-1430-63F4-2E01522A0510}.WIN32|x86.ActiveCfg = WIN32|Any CPU
- {1F06ECB1-9928-1430-63F4-2E01522A0510}.WIN32|x86.Build.0 = WIN32|Any CPU
+ {1F06ECB1-9928-1430-63F4-2E01522A0510}.WIN32|x64.ActiveCfg = Release|Any CPU
+ {1F06ECB1-9928-1430-63F4-2E01522A0510}.WIN32|x64.Build.0 = Release|Any CPU
+ {1F06ECB1-9928-1430-63F4-2E01522A0510}.WIN32|x86.ActiveCfg = Release|Any CPU
+ {1F06ECB1-9928-1430-63F4-2E01522A0510}.WIN32|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/WhackerLinkConsoleV2/AliasTools.cs b/WhackerLinkConsoleV2/AliasTools.cs
new file mode 100644
index 0000000..25584f8
--- /dev/null
+++ b/WhackerLinkConsoleV2/AliasTools.cs
@@ -0,0 +1,55 @@
+/*
+* WhackerLink - WhackerLinkConsoleV2
+*
+* 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.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+* Copyright (C) 2025 Caleb, K4PHP
+*
+*/
+
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WhackerLinkLib.Models;
+using YamlDotNet.Serialization.NamingConventions;
+using YamlDotNet.Serialization;
+using System.Diagnostics;
+
+namespace WhackerLinkConsoleV2
+{
+ public static class AliasTools
+ {
+ 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)
+ .Build();
+
+ var yamlText = File.ReadAllText(filePath);
+ return deserializer.Deserialize>(yamlText);
+ }
+
+ public static string GetAliasByRid(List aliases, int rid)
+ {
+ var match = aliases.FirstOrDefault(a => a.Rid == rid);
+ return match?.Alias;
+ }
+ }
+}
diff --git a/WhackerLinkConsoleV2/ChannelBox.xaml.cs b/WhackerLinkConsoleV2/ChannelBox.xaml.cs
index b805066..b57659f 100644
--- a/WhackerLinkConsoleV2/ChannelBox.xaml.cs
+++ b/WhackerLinkConsoleV2/ChannelBox.xaml.cs
@@ -75,9 +75,12 @@ namespace WhackerLinkConsoleV2.Controls
public MBEEncoder encoder;
public MBEDecoder decoder;
+ public MBEToneDetector toneDetector = new MBEToneDetector();
+
public P25Crypto crypter = new P25Crypto();
public bool IsReceiving { get; set; } = false;
+ public bool IsReceivingEncrypted { get; set; } = false;
public string LastSrcId
{
@@ -221,10 +224,6 @@ namespace WhackerLinkConsoleV2.Controls
PageSelectButton.IsEnabled = false;
ChannelMarkerBtn.IsEnabled = false;
}
-
- byte[] key = { 00, 00, 00, 00, 00 };
-
- crypter.AddKey(00, 0xaa, key);
}
private void ChannelBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
diff --git a/WhackerLinkConsoleV2/MBEToneDetector.cs b/WhackerLinkConsoleV2/MBEToneDetector.cs
new file mode 100644
index 0000000..2b511aa
--- /dev/null
+++ b/WhackerLinkConsoleV2/MBEToneDetector.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NWaves.Signals;
+using NWaves.Transforms;
+
+namespace WhackerLinkConsoleV2
+{
+ public class MBEToneDetector
+ {
+ // Samplerate is 8000 Hz
+ private static int sample_rate = 8000;
+
+ // We operate on 160 sample (20ms @ 8kHz) windows
+ private static int window_size = 160;
+
+ // There are 128 possible tone indexes per TIA-102.BABA-1
+ private static int num_coeffs = 128;
+
+ // Bin size in hz
+ private static float bin_size_hz = (float)sample_rate / 2f / (float)num_coeffs;
+
+ // Bounds of tone detection (in bin index format)
+ private int low_bin_limit;
+ private int high_bin_limit;
+
+ // This is the tone detection ratio (amplitude of max bin divided by average of all others)
+ private int detect_ratio;
+
+ // This is the number of "hits" on a frequency we need to get before we detect a valid tone
+ private int hits_reqd;
+
+ // Counter for the above hits
+ private int hits_freq;
+ private int num_hits;
+
+ // The STFT (short-time fourier transform) operator
+ private Stft stft;
+
+ ///
+ /// Create a pitch detector which reports the running average of pitch for a sequence of samples
+ ///
+ /// Ratio required for a valid tone detection
+ /// Number of repeated "hits" on a frequency to count as a tone detection
+ public MBEToneDetector(int detect_ratio = 90, int hits_reqd = 2, int low_limit = 250, int high_limit = 3000)
+ {
+ this.detect_ratio = detect_ratio;
+ this.hits_reqd = hits_reqd;
+ stft = new Stft(window_size, 1, NWaves.Windows.WindowType.Hann, num_coeffs);
+ hits_freq = 0;
+ num_hits = 0;
+ low_bin_limit = (int)(low_limit / bin_size_hz);
+ high_bin_limit = (int)(high_limit / bin_size_hz);
+ }
+
+ ///
+ /// Perform a tone analysis on the provided samples, and return a tone frequency if one is detected
+ ///
+ ///
+ ///
+ public int Detect(DiscreteSignal signal)
+ {
+ // Validate input
+ if (signal.Length != window_size)
+ {
+ throw new ArgumentOutOfRangeException($"Signal must be {window_size} samples long!");
+ }
+ if (signal.SamplingRate != sample_rate)
+ {
+ throw new ArgumentOutOfRangeException($"Signal must have sample rate of {sample_rate} Hz!");
+ }
+
+ // Analyze
+ float[] values = stft.Spectrogram(signal)[0];
+
+ // Remove bins outside our limit
+ float[] limited_values = values[low_bin_limit..high_bin_limit];
+
+ // Find max (from https://stackoverflow.com/a/50239922/1842613)
+ (float max_val, int max_idx) = limited_values.Select((n, i) => (n, i)).Max();
+
+ // Add back in our lower limit so the index is correct
+ max_idx += low_bin_limit;
+
+ // Calculate sum of all others
+ float sum = values.Sum() - max_val;
+
+ // Find average
+ float avg = sum / (window_size - 1);
+
+ // Find ratio
+ float ratio = max_val / avg;
+
+ // Debug
+ //Log.Logger.Debug($"(Tone detector): max at {max_idx} ({(int)(max_idx * bin_size_hz)} Hz): {max_val}, ratio: {ratio}");
+
+ // Return if above threshold
+ if (ratio > detect_ratio)
+ {
+ // Calculate the tone frequency
+ int tone_freq = (int)(bin_size_hz * max_idx);
+
+ // Determine hits
+ if (hits_freq == tone_freq)
+ {
+ num_hits++;
+ if (num_hits >= hits_reqd)
+ {
+ // Debug
+ //Log.Logger.Debug($"Detected {tone_freq} Hz tone! (ratio {ratio})");
+ return tone_freq;
+ }
+ }
+ else
+ {
+ num_hits = 1;
+ hits_freq = tone_freq;
+ }
+ }
+ return 0;
+ }
+ }
+}
diff --git a/WhackerLinkConsoleV2/MainWindow.xaml b/WhackerLinkConsoleV2/MainWindow.xaml
index 302a8c7..ef32e73 100644
--- a/WhackerLinkConsoleV2/MainWindow.xaml
+++ b/WhackerLinkConsoleV2/MainWindow.xaml
@@ -11,7 +11,7 @@
-
+
@@ -42,7 +42,7 @@
-
+
@@ -162,6 +162,19 @@
+