commit 9452a5c84056cea70dfd9738c0f17e585558f6d1 Author: php Date: Sun Oct 6 02:58:10 2024 -0500 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8dd4607 --- /dev/null +++ b/.gitignore @@ -0,0 +1,398 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml \ No newline at end of file diff --git a/dvmusrp.sln b/dvmusrp.sln new file mode 100644 index 0000000..5cb567c --- /dev/null +++ b/dvmusrp.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35208.52 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dvmusrp", "dvmusrp\dvmusrp.csproj", "{A2F59272-70E6-4B12-8C6C-9410761B2D3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2F59272-70E6-4B12-8C6C-9410761B2D3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2F59272-70E6-4B12-8C6C-9410761B2D3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2F59272-70E6-4B12-8C6C-9410761B2D3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2F59272-70E6-4B12-8C6C-9410761B2D3E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {47B80728-36B9-4500-88DE-12147179ED6E} + EndGlobalSection +EndGlobal diff --git a/dvmusrp/Config.cs b/dvmusrp/Config.cs new file mode 100644 index 0000000..483a524 --- /dev/null +++ b/dvmusrp/Config.cs @@ -0,0 +1,45 @@ +using YamlDotNet.Serialization; +using System.IO; +using YamlDotNet.Serialization.NamingConventions; + +namespace dvmusrp +{ + public class Config + { + public DvmConfig dvm = new DvmConfig(); + public UsrpConfig usrp = new UsrpConfig(); + + public Config() { /* stub */ } + + public static Config Load(string filePath) + { + var yamlContent = File.ReadAllText(filePath); + var deserializer = new DeserializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .Build(); + return deserializer.Deserialize(yamlContent); + } + } + + public class DvmConfig + { + public string ReceiveAddress { get; set; } + public string SendAddress { get; set; } + + public int ReceivePort { get; set; } + public int SendPort { get; set; } + + public DvmConfig() { /* sub */ } + } + + public class UsrpConfig + { + public string ReceiveAddress { get; set; } + public string SendAddress { get; set; } + + public int ReceivePort { get; set; } + public int SendPort { get; set; } + + public UsrpConfig() { /* sub */ } + } +} diff --git a/dvmusrp/Network.DVM.cs b/dvmusrp/Network.DVM.cs new file mode 100644 index 0000000..0ffe816 --- /dev/null +++ b/dvmusrp/Network.DVM.cs @@ -0,0 +1,67 @@ +using System.Net.Sockets; +using System.Text; + +namespace dvmusrp +{ + public class NetworkDVM : Network + { + private NetworkUSRP usrpNetwork; + + public NetworkDVM(int receivePort, int sendPort, string receiveAddress, string sendAddress) : base(receivePort, sendPort, receiveAddress, sendAddress) { } + + public void SetUSRPNetwork(NetworkUSRP usrpNetwork) + { + this.usrpNetwork = usrpNetwork; + } + + public override void Start() + { + Task.Run(async () => + { + using (udpClient = new UdpClient(receivePort)) + { + while (true) + { + try + { + var receivedResult = await udpClient.ReceiveAsync(); + + ForwardToUSRP(receivedResult.Buffer); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + } + } + }); + } + + private void ForwardToUSRP(byte[] packet) + { + byte[] usrpData = new byte[352]; + byte[] audio = new byte[320]; + byte[] usrpHeader = new byte[32]; + + if (packet.Length != 324) + { + Console.WriteLine("DVM meta data is not yet supported"); + return; + } + + Array.Copy(Encoding.ASCII.GetBytes("USRP"), usrpHeader, 4); + Array.Copy(usrpHeader, usrpData, usrpHeader.Length); + Array.Copy(packet, 4, audio, 0, audio.Length); + Array.Copy(audio, 0, usrpData, 32, audio.Length); + + // Console.WriteLine(Utils.HexDump(usrpData)); + + usrpNetwork.SendData(usrpData); + } + + public void SendData(byte[] data) + { + Send(data, sendAddress, sendPort); + } + } +} diff --git a/dvmusrp/Network.USRP.cs b/dvmusrp/Network.USRP.cs new file mode 100644 index 0000000..e74ae9f --- /dev/null +++ b/dvmusrp/Network.USRP.cs @@ -0,0 +1,70 @@ +using System.Net.Sockets; +using System.Text; + +namespace dvmusrp +{ + public class NetworkUSRP : Network + { + private NetworkDVM dvmNetwork; + + public NetworkUSRP(int receivePort, int sendPort, string receiveAddress, string sendAddress) : base(receivePort, sendPort, receiveAddress, sendAddress) { } + + public void SetDVMNetwork(NetworkDVM dvmNetwork) + { + this.dvmNetwork = dvmNetwork; + } + + public override void Start() + { + Task.Run(async () => + { + using (udpClient = new UdpClient(receivePort)) + { + while (true) + { + try + { + var receivedResult = await udpClient.ReceiveAsync(); + + ForwardToDVM(receivedResult.Buffer); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + } + } + }); + } + + private void ForwardToDVM(byte[] packet) + { + // TODO: Parse meta data for possible DVM use + byte[] audio = new byte[324]; + byte[] usrpHeader = new byte[32]; + + Array.Copy(packet, usrpHeader, usrpHeader.Length); + + if (packet.Length != 352) + return; + + Array.Copy(packet, 32, audio, 4, audio.Length - 4); + + if (audio.Length != 324) + { + Console.WriteLine(audio.Length); + } + + Utils.WriteBytes(320, ref audio, 0); + + // Console.WriteLine(Utils.HexDump(audio)); + + dvmNetwork.SendData(audio); + } + + public void SendData(byte[] data) + { + Send(data, sendAddress, sendPort); + } + } +} diff --git a/dvmusrp/Network.cs b/dvmusrp/Network.cs new file mode 100644 index 0000000..b6a30ea --- /dev/null +++ b/dvmusrp/Network.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace dvmusrp +{ + public abstract class Network + { + protected int receivePort; + protected int sendPort; + + protected string receiveAddress; + protected string sendAddress; + + protected UdpClient udpClient; + + protected Network(int receivePort, int sendPort, string receiveAddress, string sendAddress) + { + this.receivePort = receivePort; + this.sendPort = sendPort; + this.receiveAddress = receiveAddress; + this.sendAddress = sendAddress; + } + + public abstract void Start(); + + protected void Send(byte[] data, string destinationIp, int destinationPort) + { + using (var client = new UdpClient()) + { + client.Send(data, data.Length, destinationIp, destinationPort); + } + } + } +} diff --git a/dvmusrp/Program.cs b/dvmusrp/Program.cs new file mode 100644 index 0000000..35e88e1 --- /dev/null +++ b/dvmusrp/Program.cs @@ -0,0 +1,40 @@ +using dvmusrp; + +internal class Program +{ + static async Task Main(string[] args) + { + var configFilePath = GetConfigFilePath(args); + + if (string.IsNullOrEmpty(configFilePath)) + { + Console.WriteLine("Usage: dvmusrp -c configfile.yml"); + return; + } + + var config = Config.Load(configFilePath); + + var usrpNetwork = new NetworkUSRP(config.usrp.ReceivePort, config.usrp.SendPort, config.usrp.ReceiveAddress, config.usrp.SendAddress); + var dvmNetwork = new NetworkDVM(config.dvm.ReceivePort, config.dvm.SendPort, config.dvm.ReceiveAddress, config.dvm.ReceiveAddress); + + usrpNetwork.SetDVMNetwork(dvmNetwork); + dvmNetwork.SetUSRPNetwork(usrpNetwork); + + usrpNetwork.Start(); + dvmNetwork.Start(); + + await Task.Delay(Timeout.Infinite); + } + + static string GetConfigFilePath(string[] args) + { + for (int i = 0; i < args.Length; i++) + { + if (args[i] == "-c" && i + 1 < args.Length) + { + return args[i + 1]; + } + } + return null; + } +} diff --git a/dvmusrp/Utils.cs b/dvmusrp/Utils.cs new file mode 100644 index 0000000..155b224 --- /dev/null +++ b/dvmusrp/Utils.cs @@ -0,0 +1,922 @@ +// SPDX-License-Identifier: AGPL-3.0-only +/** +* Digital Voice Modem - Fixed Network Equipment Core Library +* AGPLv3 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Fixed Network Equipment Core Library +* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0) +* +* Copyright (C) 2022-2024 Bryan Biedenkapp, N2PLL +* Copyright (C) 2024 Caleb, KO4UYJ +* +*/ + +using System; +using System.Linq; +using System.Security.Cryptography; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using System.Text; + +namespace dvmusrp +{ + /// + /// + /// + public class Utils + { + private static readonly byte[] BIT_MASK_TABLE = new byte[8] { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + private static Action memsetDelegate; + + /* + ** Methods + */ + + /// + /// Static initializer for the class. + /// + static Utils() + { + DynamicMethod dynamicMethod = new DynamicMethod("Memset", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, + null, new[] { typeof(IntPtr), typeof(byte), typeof(int) }, typeof(Utils), true); + + ILGenerator generator = dynamicMethod.GetILGenerator(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldarg_2); + generator.Emit(OpCodes.Initblk); + generator.Emit(OpCodes.Ret); + + memsetDelegate = (Action)dynamicMethod.CreateDelegate(typeof(Action)); + } + + /// + /// + /// + /// + /// + public static ushort DoReverseEndian(ushort x) + { + //return Convert.ToUInt16((x << 8 & 0xff00) | (x >> 8)); + return BitConverter.ToUInt16(BitConverter.GetBytes(x).Reverse().ToArray(), 0); + } + + /// + /// + /// + /// + /// + public static uint DoReverseEndian(uint x) + { + //return (x << 24 | (x & 0xff00) << 8 | (x & 0xff0000) >> 8 | x >> 24); + return BitConverter.ToUInt32(BitConverter.GetBytes(x).Reverse().ToArray(), 0); + } + + /// + /// + /// + /// + /// + public static ulong DoReverseEndian(ulong x) + { + //return (x << 56 | (x & 0xff00) << 40 | (x & 0xff0000) << 24 | (x & 0xff000000) << 8 | (x & 0xff00000000) >> 8 | (x & 0xff0000000000) >> 24 | (x & 0xff000000000000) >> 40 | x >> 56); + return BitConverter.ToUInt64(BitConverter.GetBytes(x).Reverse().ToArray(), 0); + } + + /// + /// + /// + /// + /// + public static int DoReverseEndian(int x) + { + return BitConverter.ToInt32(BitConverter.GetBytes(x).Reverse().ToArray(), 0); + } + + /// + /// + /// + /// + /// + /// + public static void Memset(byte[] array, byte what, int length) + { + GCHandle gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned); + memsetDelegate(gcHandle.AddrOfPinnedObject(), what, length); + gcHandle.Free(); + } + + /// + /// + /// + /// + /// + /// + public static void ByteToBitsBE(byte b, ref bool[] bits, int offset) + { + bits[0 + offset] = (b & 0x80U) == 0x80U; + bits[1 + offset] = (b & 0x40U) == 0x40U; + bits[2 + offset] = (b & 0x20U) == 0x20U; + bits[3 + offset] = (b & 0x10U) == 0x10U; + bits[4 + offset] = (b & 0x08U) == 0x08U; + bits[5 + offset] = (b & 0x04U) == 0x04U; + bits[6 + offset] = (b & 0x02U) == 0x02U; + bits[7 + offset] = (b & 0x01U) == 0x01U; + } + + /// + /// + /// + /// + /// + /// + public static void ByteToBitsLE(byte b, ref bool[] bits, int offset) + { + bits[0 + offset] = (b & 0x01U) == 0x01U; + bits[1 + offset] = (b & 0x02U) == 0x02U; + bits[2 + offset] = (b & 0x04U) == 0x04U; + bits[3 + offset] = (b & 0x08U) == 0x08U; + bits[4 + offset] = (b & 0x10U) == 0x10U; + bits[5 + offset] = (b & 0x20U) == 0x20U; + bits[6 + offset] = (b & 0x40U) == 0x40U; + bits[7 + offset] = (b & 0x80U) == 0x80U; + } + + /// + /// + /// + /// + /// + /// + public static void BitsToByteBE(bool[] bits, int offset, ref byte b) + { + b = (byte)(bits[0 + offset] ? 0x80U : 0x00U); + b |= (byte)(bits[1 + offset] ? 0x40U : 0x00U); + b |= (byte)(bits[2 + offset] ? 0x20U : 0x00U); + b |= (byte)(bits[3 + offset] ? 0x10U : 0x00U); + b |= (byte)(bits[4 + offset] ? 0x08U : 0x00U); + b |= (byte)(bits[5 + offset] ? 0x04U : 0x00U); + b |= (byte)(bits[6 + offset] ? 0x02U : 0x00U); + b |= (byte)(bits[7 + offset] ? 0x01U : 0x00U); + } + + /// + /// + /// + /// + /// + /// + public static void BitsToByteLE(bool[] bits, int offset, ref byte b) + { + b = (byte)(bits[0 + offset] ? 0x01U : 0x00U); + b |= (byte)(bits[1 + offset] ? 0x02U : 0x00U); + b |= (byte)(bits[2 + offset] ? 0x04U : 0x00U); + b |= (byte)(bits[3 + offset] ? 0x08U : 0x00U); + b |= (byte)(bits[4 + offset] ? 0x10U : 0x00U); + b |= (byte)(bits[5 + offset] ? 0x20U : 0x00U); + b |= (byte)(bits[6 + offset] ? 0x40U : 0x00U); + b |= (byte)(bits[7 + offset] ? 0x80U : 0x00U); + } + + /// + /// + /// + /// + /// + /// + public static void WriteBit(ref byte[] p, uint i, bool b) + { + p[(i) >> 3] = (byte)((b) ? (p[(i) >> 3] | BIT_MASK_TABLE[(i) & 7]) : (p[(i) >> 3] & ~BIT_MASK_TABLE[(i) & 7])); + } + + /// + /// + /// + /// + /// + /// + public static void WriteBit(ref Span p, uint i, bool b) + { + p[(int)((i) >> 3)] = (byte)((b) ? (p[(int)((i) >> 3)] | BIT_MASK_TABLE[(i) & 7]) : (p[(int)((i) >> 3)] & ~BIT_MASK_TABLE[(i) & 7])); + } + + /// + /// + /// + /// + /// + /// + public static bool ReadBit(byte[] p, uint i) + { + return (p[(i) >> 3] & BIT_MASK_TABLE[(i) & 7]) != 0; + } + + /// + /// + /// + /// + /// + /// + public static bool ReadBit(Span p, uint i) + { + return (p[(int)((i) >> 3)] & BIT_MASK_TABLE[(i) & 7]) != 0; + } + + /// + /// Write the given bytes in the unsigned short into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(ushort val, ref byte[] buffer, int offset) + { + buffer[offset] = (byte)(val >> 8); + buffer[offset + 1] = (byte)(val & 0xFF); + } + + /// + /// Write the given bytes in the unsigned short into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(ushort val, ref Span buffer, int offset) + { + buffer[offset] = (byte)(val >> 8); + buffer[offset + 1] = (byte)(val & 0xFF); + } + + /// + /// Write the given bytes in the unsigned integer into the given buffer (by most significant byte) + /// + /// + public static void Write3Bytes(uint val, ref byte[] buffer, int offset) + { + buffer[offset + 0] = (byte)((val >> 16) & 0xFF); + buffer[offset + 1] = (byte)((val >> 8) & 0xFF); + buffer[offset + 2] = (byte)(val & 0xFF); + } + + /// + /// Write the given bytes in the unsigned integer into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(uint val, ref byte[] buffer, int offset) + { + buffer[offset] = (byte)((val >> 24) & 0xFF); + buffer[offset + 1] = (byte)((val >> 16) & 0xFF); + buffer[offset + 2] = (byte)((val >> 8) & 0xFF); + buffer[offset + 3] = (byte)(val & 0xFF); + } + + /// + /// Write the given bytes in the unsigned integer into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(uint val, ref Span buffer, int offset) + { + buffer[offset] = (byte)((val >> 24) & 0xFF); + buffer[offset + 1] = (byte)((val >> 16) & 0xFF); + buffer[offset + 2] = (byte)((val >> 8) & 0xFF); + buffer[offset + 3] = (byte)(val & 0xFF); + } + + /// + /// Write the given bytes in the unsigned long into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(ulong val, ref byte[] buffer, int offset) + { + buffer[offset] = (byte)((val >> 56) & 0xFF); + buffer[offset + 1] = (byte)((val >> 48) & 0xFF); + buffer[offset + 2] = (byte)((val >> 40) & 0xFF); + buffer[offset + 3] = (byte)((val >> 32) & 0xFF); + buffer[offset + 4] = (byte)((val >> 24) & 0xFF); + buffer[offset + 5] = (byte)((val >> 16) & 0xFF); + buffer[offset + 6] = (byte)((val >> 8) & 0xFF); + buffer[offset + 7] = (byte)(val & 0xFF); + } + + /// + /// Write the given bytes in the unsigned long into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(ulong val, ref Span buffer, int offset) + { + buffer[offset] = (byte)((val >> 56) & 0xFF); + buffer[offset + 1] = (byte)((val >> 48) & 0xFF); + buffer[offset + 2] = (byte)((val >> 40) & 0xFF); + buffer[offset + 3] = (byte)((val >> 32) & 0xFF); + buffer[offset + 4] = (byte)((val >> 24) & 0xFF); + buffer[offset + 5] = (byte)((val >> 16) & 0xFF); + buffer[offset + 6] = (byte)((val >> 8) & 0xFF); + buffer[offset + 7] = (byte)(val & 0xFF); + } + + /// + /// Write the given bytes in the short into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(short val, ref byte[] buffer, int offset) + { + WriteBytes((ushort)val, ref buffer, offset); + } + + /// + /// Write the given bytes in the short into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(short val, ref Span buffer, int offset) + { + WriteBytes((ushort)val, ref buffer, offset); + } + + /// + /// Write the given bytes in the integer into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(int val, ref byte[] buffer, int offset) + { + WriteBytes((uint)val, ref buffer, offset); + } + + /// + /// Write the given bytes in the integer into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(int val, ref Span buffer, int offset) + { + WriteBytes((uint)val, ref buffer, offset); + } + + /// + /// Write the given bytes in the long into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(long val, ref byte[] buffer, int offset) + { + WriteBytes((ulong)val, ref buffer, offset); + } + + /// + /// Write the given bytes in the long into the given buffer (by most significant byte) + /// + /// + /// + /// + public static void WriteBytes(long val, ref Span buffer, int offset) + { + WriteBytes((ulong)val, ref buffer, offset); + } + + /// + /// Get an unsigned short value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static ushort ToUInt16(byte[] buffer, int offset) + { + return (ushort)(((buffer[offset] << 8) & 0xFF00) | ((buffer[offset + 1] << 0) & 0x00FF)); + } + + /// + /// Get an unsigned short value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static ushort ToUInt16(Span buffer, int offset) + { + return (ushort)(((buffer[offset] << 8) & 0xFF00) | ((buffer[offset + 1] << 0) & 0x00FF)); + } + + /// + /// Get an unsigned integer value from the given bytes. (by most significant byte) + /// + /// + public static uint Bytes3ToUInt32(byte[] buffer, int offset) + { + uint val = (uint)(((buffer[offset] << 16) & 0x00FF0000U) | ((buffer[offset + 1] << 8) & 0x0000FF00U) | + ((buffer[offset + 2] << 0) & 0x000000FFU)); + return val; + } + + /// + /// Get an unsigned integer value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static uint ToUInt32(byte[] buffer, int offset) + { + uint val = (uint)(((buffer[offset + 0] << 24) & 0xFF000000U) | ((buffer[offset + 1] << 16) & 0x00FF0000U) + | ((buffer[offset + 2] << 8) & 0x0000FF00U) | ((buffer[offset + 3] << 0) & 0x000000FFU)); + return val; + } + + /// + /// Get an unsigned integer value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static uint ToUInt32(Span buffer, int offset) + { + uint val = (uint)(((buffer[offset + 0] << 24) & 0xFF000000U) | ((buffer[offset + 1] << 16) & 0x00FF0000U) + | ((buffer[offset + 2] << 8) & 0x0000FF00U) | ((buffer[offset + 3] << 0) & 0x000000FFU)); + return val; + } + + /// + /// Get an unsigned long value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static ulong ToUInt64(byte[] buffer, int offset) + { + return (((ulong)ToUInt32(buffer, offset + 0)) << 32) | ToUInt32(buffer, offset + 4); + } + + /// + /// Get an unsigned long value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static ulong ToUInt64(Span buffer, int offset) + { + return (((ulong)ToUInt32(buffer, offset + 0)) << 32) | ToUInt32(buffer, offset + 4); + } + + /// + /// Get an signed short value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static short ToInt16(byte[] buffer, int offset) + { + return (short)ToUInt16(buffer, offset); + } + + /// + /// Get an signed short value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static short ToInt16(Span buffer, int offset) + { + return (short)ToUInt16(buffer, offset); + } + + /// + /// Get a signed integer value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static int ToInt32(byte[] buffer, int offset) + { + return (int)ToUInt32(buffer, offset); + } + + /// + /// Get a signed integer value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static int ToInt32(Span buffer, int offset) + { + return (int)ToUInt32(buffer, offset); + } + + /// + /// Get a signed long value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static long ToInt64(byte[] buffer, int offset) + { + return (long)ToUInt64(buffer, offset); + } + + /// + /// Get a signed long value from the given bytes. (by most significant byte) + /// + /// + /// + /// + public static long ToInt64(Span buffer, int offset) + { + return (long)ToUInt64(buffer, offset); + } + + /// + /// + /// + /// + /// + /// + public static byte BIN2HEX(byte[] input, uint offset) + { + byte output = 0x00; + + output |= (byte)(ReadBit(input, offset + 0U) ? 0x20U : 0x00U); + output |= (byte)(ReadBit(input, offset + 1U) ? 0x10U : 0x00U); + output |= (byte)(ReadBit(input, offset + 2U) ? 0x08U : 0x00U); + output |= (byte)(ReadBit(input, offset + 3U) ? 0x04U : 0x00U); + output |= (byte)(ReadBit(input, offset + 4U) ? 0x02U : 0x00U); + output |= (byte)(ReadBit(input, offset + 5U) ? 0x01U : 0x00U); + + return output; + } + + /// + /// + /// + /// + /// + /// + /// + public static void HEX2BIN(byte input, ref byte[] output, uint offset) + { + WriteBit(ref output, offset + 0U, (input & 0x20U) == 0x20U); + WriteBit(ref output, offset + 1U, (input & 0x10U) == 0x10U); + WriteBit(ref output, offset + 2U, (input & 0x08U) == 0x08U); + WriteBit(ref output, offset + 3U, (input & 0x04U) == 0x04U); + WriteBit(ref output, offset + 4U, (input & 0x02U) == 0x02U); + WriteBit(ref output, offset + 5U, (input & 0x01U) == 0x01U); + } + + /// + /// Primitive conversion from Unicode to ASCII that preserves special characters. + /// + /// The string to convert. + /// The buffer to fill. + /// The start of the string in the buffer. + /// The number of characters to convert. + /// The built-in ASCIIEncoding converts characters of codepoint > 127 to ?, + /// this preserves those code points by removing the top 16 bits of each character. + public static void StringToBytes(string value, byte[] dest, int offset, int count) + { + char[] chars = value.ToCharArray(); + + int i = 0; + while (i < chars.Length) + { + dest[i + offset] = (byte)chars[i]; + ++i; + } + + while (i < count) + { + dest[i + offset] = 0; + ++i; + } + } + + /// + /// Primitive conversion from ASCII to Unicode that preserves special characters. + /// + /// The data to convert. + /// The first byte to convert. + /// The number of bytes to convert. + /// The string. + /// The built-in ASCIIEncoding converts characters of codepoint > 127 to ?, + /// this preserves those code points. + public static string BytesToString(byte[] data, int offset, int count) + { + char[] result = new char[count]; + + // iterate through the individual bytes, and convert them to a character + for (int i = 0; i < count; ++i) + result[i] = (char)data[i + offset]; + + return new string(result); + } + + /// + /// Converts a hexadecimal string to a byte array. + /// + /// The hexadecimal string. + /// The byte array. + public static byte[] HexStringToByteArray(string hexString) + { + // Remove any spaces or non-hex characters if necessary + hexString = hexString.Replace(" ", "").Replace("-", ""); + + // Ensure the string length is even + if (hexString.Length % 2 != 0) + throw new ArgumentException("Invalid hex string length."); + + byte[] byteArray = new byte[hexString.Length / 2]; + + for (int i = 0; i < hexString.Length; i += 2) + { + byteArray[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16); + } + + return byteArray; + } + + /// + /// Converts a hexadecimal string to a byte array for preshared key. + /// + /// The hexadecimal string. + /// The expected key length in bytes. + /// The byte array. + public static byte[] ConvertHexStringToPresharedKey(string hexString) + { + if (hexString.Length == 32) + { + // Double the key if it's 32 characters (16 hex pairs) + hexString += hexString; + Console.WriteLine("Half-length network preshared encryption key detected, doubling key on itself."); + } + + if (hexString.Length == 64) + { + if (hexString.All(c => "0123456789abcdefABCDEF".Contains(c))) + { + return HexStringToByteArray(hexString); + } + else + { + throw new ArgumentException("Invalid characters in the network preshared encryption key."); + } + } + else + { + throw new ArgumentException("Invalid network preshared encryption key length, key should be 32 hex pairs, or 64 characters."); + } + } + + /// + /// Helper to display the ASCII representation of a hex dump. + /// + /// + /// + /// + private static string DisplayHexChars(Span buffer, int offset) + { + int bCount = 0; + + string _out = string.Empty; + for (int i = offset; i < buffer.Length; i++) + { + // stop every 16 bytes... + if (bCount == 16) + break; + + byte b = buffer[i]; + char c = Convert.ToChar(b); + + // make control and illegal characters spaces + if (c >= 0x00 && c <= 0x1F) + c = ' '; + if (c >= 0x7F) + c = ' '; + + _out += c; + + bCount++; + } + + return _out; + } + + /// + /// Helper to display the ASCII representation of a hex dump. + /// + /// + /// + /// + private static string DisplayHexChars(byte[] buffer, int offset) + { + int bCount = 0; + + string _out = string.Empty; + for (int i = offset; i < buffer.Length; i++) + { + // stop every 16 bytes... + if (bCount == 16) + break; + + byte b = buffer[i]; + char c = Convert.ToChar(b); + + // make control and illegal characters spaces + if (c >= 0x00 && c <= 0x1F) + c = ' '; + if (c >= 0x7F) + c = ' '; + + _out += c; + + bCount++; + } + + return _out; + } + + /// + /// Perform a hex dump of a buffer. + /// + /// + /// + public static string HexDump(byte[] buffer, int offset = 0) + { + int bCount = 0, j = 0; + + // iterate through buffer printing all the stored bytes + string res = "\n\tDUMP " + j.ToString("X4") + ": "; + for (int i = offset; i < buffer.Length; i++) + { + byte b = buffer[i]; + + // split the message every 16 bytes... + if (bCount == 16) + { + res += " *" + DisplayHexChars(buffer, j) + "*\n"; + bCount = 0; + j += 16; + res += "\tDUMP " + j.ToString("X4") + ": "; + } + else + res += (bCount > 0) ? " " : ""; + + res += b.ToString("X2"); + bCount++; + } + + // if the byte count at this point is non-zero print the message + if (bCount != 0) + { + if (bCount < 16) + { + for (int i = bCount; i < 16; i++) + res += " "; + } + + res += " *" + DisplayHexChars(buffer, j) + "*"; + } + + return res; + } + + /// + /// Perform a hex dump of a buffer. + /// + /// + /// + public static string HexDump(short[] buffer, int offset = 0) + { + int bCount = 0, j = 0; + + // iterate through buffer printing all the stored bytes + string res = "\n\tDUMP " + j.ToString("X4") + ": "; + for (int i = offset; i < buffer.Length; i++) + { + short b = buffer[i]; + + // split the message every 16 bytes... + if (bCount == 16) + { + //res += " *" + DisplayHexChars(buffer, j) + "*\n"; + res += "\n"; + bCount = 0; + j += 16; + res += "\tDUMP " + j.ToString("X4") + ": "; + } + else + res += (bCount > 0) ? " " : ""; + + res += b.ToString("X4"); + bCount++; + } + + // if the byte count at this point is non-zero print the message + if (bCount != 0) + { + if (bCount < 16) + { + for (int i = bCount; i < 16; i++) + res += " "; + } + + //res += " *" + DisplayHexChars(buffer, j) + "*"; + } + + return res; + } + + /// + /// Perform a hex dump of a buffer. + /// + /// + /// + public static string HexDump(Memory buffer, int offset = 0) + { + int bCount = 0, j = 0; + + // iterate through buffer printing all the stored bytes + string res = "\n\tDUMP " + j.ToString("X4") + ": "; + for (int i = offset; i < buffer.Length; i++) + { + byte b = buffer.Span[i]; + + // split the message every 16 bytes... + if (bCount == 16) + { + res += " *" + DisplayHexChars(buffer.Span, j) + "*\n"; + bCount = 0; + j += 16; + res += "\tDUMP " + j.ToString("X4") + ": "; + } + else + res += (bCount > 0) ? " " : ""; + + res += b.ToString("X2"); + bCount++; + } + + // if the byte count at this point is non-zero print the message + if (bCount != 0) + { + if (bCount < 16) + { + for (int i = bCount; i < 16; i++) + res += " "; + } + + res += " *" + DisplayHexChars(buffer.Span, j) + "*"; + } + + return res; + } + + /// + /// + /// + /// + /// + public static byte[] sha256_hash(string value) + { + return sha256_hash(Encoding.ASCII.GetBytes(value)); + } + + /// + /// + /// + /// + /// + public static byte[] sha256_hash(byte[] value) + { + using (SHA256 hash = SHA256.Create()) + return hash.ComputeHash(value); + } + + /// + /// + /// + /// + /// + /// + public static uint CountBits(uint v) + { + uint count = 0U; + while (v != 0U) + { + v &= v - 1U; + count++; + } + + return count; + } + } // public class Utils +} // namespace dvmusrp \ No newline at end of file diff --git a/dvmusrp/config.example.yml b/dvmusrp/config.example.yml new file mode 100644 index 0000000..cfeda01 --- /dev/null +++ b/dvmusrp/config.example.yml @@ -0,0 +1,11 @@ +dvm: + receiveAddress: 127.0.0.1 + receivePort: 33000 + sendPort: 35000 + sendAddress: 127.0.0.1 + +usrp: + receiveAddress: 0.0.0.0 + receivePort: 34001 + sendPort: 32001 + sendAddress: 192.168.198.131 \ No newline at end of file diff --git a/dvmusrp/dvmusrp.csproj b/dvmusrp/dvmusrp.csproj new file mode 100644 index 0000000..91bab76 --- /dev/null +++ b/dvmusrp/dvmusrp.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + disable + + + + + + +