diff --git a/Software/LogicAnalyzer/AppConfig.json b/Software/LogicAnalyzer/AppConfig.json index 8703ba3..a8f7ee0 100644 --- a/Software/LogicAnalyzer/AppConfig.json +++ b/Software/LogicAnalyzer/AppConfig.json @@ -1,19 +1,9 @@ { - "AppName": "Avalonia.Designer.HostApp, Version=0.10.16.0, Culture=neutral, PublicKeyToken=c8d484a7012f9a8b", + "AppName": "LogicAnalyzer, Version=3.5.0.1, Culture=neutral, PublicKeyToken=null", "WindowSettings": { - "LogicAnalyzer.Dialogs.CreateSamplesDialog": { - "Width": 800.0, - "Height": 600.0, - "Position": { - "$type": "Avalonia.PixelPoint, Avalonia.Visuals", - "X": 0, - "Y": 0 - }, - "WindowState": 0 - }, "LogicAnalyzer.MainWindow": { - "Width": 1024.0, - "Height": 800.0, + "Width": 1837.0, + "Height": 1083.0, "Position": { "$type": "Avalonia.PixelPoint, Avalonia.Visuals", "X": 0, @@ -22,13 +12,23 @@ "WindowState": 0 }, "LogicAnalyzer.Dialogs.SignalComposerDialog": { - "EditorFontSize": 14.0, - "Width": 640.0, - "Height": 480.0, + "EditorFontSize": 19.0, + "Width": 973.0, + "Height": 415.0, "Position": { "$type": "Avalonia.PixelPoint, Avalonia.Visuals", - "X": 0, - "Y": 0 + "X": 1280, + "Y": 1016 + }, + "WindowState": 0 + }, + "LogicAnalyzer.Dialogs.CreateSamplesDialog": { + "Width": 800.0, + "Height": 600.0, + "Position": { + "$type": "Avalonia.PixelPoint, Avalonia.Visuals", + "X": 1665, + "Y": 1233 }, "WindowState": 0 }, @@ -37,8 +37,8 @@ "Height": 450.0, "Position": { "$type": "Avalonia.PixelPoint, Avalonia.Visuals", - "X": 0, - "Y": 0 + "X": 1629, + "Y": 793 }, "WindowState": 0 } diff --git a/Software/LogicAnalyzer/Artwork/Logo40.jpg b/Software/LogicAnalyzer/Artwork/Logo40.jpg new file mode 100644 index 0000000..8e55d49 Binary files /dev/null and b/Software/LogicAnalyzer/Artwork/Logo40.jpg differ diff --git a/Software/LogicAnalyzer/CLCapture/CLCapture.csproj b/Software/LogicAnalyzer/CLCapture/CLCapture.csproj index b56f98c..4f6bbef 100644 --- a/Software/LogicAnalyzer/CLCapture/CLCapture.csproj +++ b/Software/LogicAnalyzer/CLCapture/CLCapture.csproj @@ -6,7 +6,7 @@ enable enable window.ico - 4.0.0.0 + 4.5.1.0 diff --git a/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs b/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs index bc304bf..074edd2 100644 --- a/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs +++ b/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs @@ -16,7 +16,7 @@ namespace CLCapture public string? AddressPort { get; set; } [Value(1, Required = true, HelpText = "Desired sampling frequency.")] public int SamplingFrequency { get; set; } - [Value(2, Required = true, HelpText = "List of channels to capture (channels sepparated by comma).")] + [Value(2, Required = true, HelpText = "List of channels to capture (channels sepparated by comma, can contain a name adding a semicolon after the channel number, no spaces allowed).")] public string? Channels { get; set; } [Value(3, Required = true, HelpText = "Number of samples to capture before the trigger.")] public int PreSamples { get; set; } diff --git a/Software/LogicAnalyzer/CLCapture/CLChannel.cs b/Software/LogicAnalyzer/CLCapture/CLChannel.cs new file mode 100644 index 0000000..607153c --- /dev/null +++ b/Software/LogicAnalyzer/CLCapture/CLChannel.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CLCapture +{ + public class CLChannel + { + public CLChannel(string Definition) + { + if(string.IsNullOrWhiteSpace(Definition)) + throw new ArgumentNullException("Missing channel definition."); + + var inputParts = Definition.Trim().Split(":"); + + if (inputParts.Length < 1) + throw new ArgumentException("Invalid channel definition"); + + if (inputParts.Length == 1) + { + int value; + + if (!int.TryParse(inputParts[0], out value)) + throw new ArgumentException("Invalid channel definition, channel must be defined in decimal form."); + + ChannelNumber = value; + ChannelName = $"Channel {value}"; + } + else if (inputParts.Length == 2) + { + int value; + + if (!int.TryParse(inputParts[0], out value)) + throw new ArgumentException("Invalid channel definition, channel must be defined in decimal form."); + + ChannelNumber = value; + ChannelName = inputParts[1]; + } + else + { + throw new ArgumentException("Invalid channel definition, too many parts."); + } + } + public int ChannelNumber { get; set; } + public string ChannelName { get; set; } + } +} diff --git a/Software/LogicAnalyzer/CLCapture/Program.cs b/Software/LogicAnalyzer/CLCapture/Program.cs index 4407c27..3ed7d9c 100644 --- a/Software/LogicAnalyzer/CLCapture/Program.cs +++ b/Software/LogicAnalyzer/CLCapture/Program.cs @@ -39,15 +39,27 @@ async Task Capture(CLCaptureOptions opts) return -1; } - int[]? channels = opts.Channels?.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(c => int.Parse(c)).ToArray(); + CLChannel[]? channels; - if (channels == null || channels.Any(c => c < 1 || c > 24)) + try + { + + //int[]? channels = opts.Channels?.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(c => int.Parse(c)).ToArray(); + channels = opts.Channels?.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(c => new CLChannel(c)).ToArray(); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + return -1; + } + + if (channels == null || channels.Any(c => c.ChannelNumber < 1 || c.ChannelNumber > 24)) { Console.WriteLine("Specified capture channels out of range."); return -1; } - int maxChannel = channels.Max(); + int maxChannel = channels.Max(c => c.ChannelNumber); int channelMode = maxChannel <= 8 ? 0 : (maxChannel <= 16 ? 1 : 2); int channelCount = maxChannel <= 8 ? 8 : (maxChannel <= 16 ? 16 : 24); @@ -163,13 +175,13 @@ async Task Capture(CLCaptureOptions opts) captureCompletedTask = new TaskCompletionSource(); - channels = opts.Channels.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(c => int.Parse(c) - 1).ToArray(); + int[] nChannels = channels.Select(c => c.ChannelNumber - 1).ToArray(); if (opts.Trigger.TriggerType == CLTriggerType.Edge) { Console.WriteLine("Starting edge triggered capture..."); var resStart = driver.StartCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples, - channels, opts.Trigger.Channel - 1, opts.Trigger.Value == "0", CaptureFinished); + nChannels, opts.Trigger.Channel - 1, opts.Trigger.Value == "0", CaptureFinished); if (resStart != CaptureError.None) { @@ -209,7 +221,7 @@ async Task Capture(CLCaptureOptions opts) } var resStart = driver.StartPatternCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples, - channels, opts.Trigger.Channel - 1, bitCount, triggerPattern, opts.Trigger.TriggerType == CLTriggerType.Fast, CaptureFinished); + nChannels, opts.Trigger.Channel - 1, bitCount, triggerPattern, opts.Trigger.TriggerType == CLTriggerType.Fast, CaptureFinished); if (resStart != CaptureError.None) { @@ -246,7 +258,7 @@ async Task Capture(CLCaptureOptions opts) var file = File.Create(opts.OutputFile); StreamWriter sw = new StreamWriter(file); - sw.WriteLine(String.Join(',', channels.Select(c => $"Channel {c + 1}").ToArray())); + sw.WriteLine(String.Join(',', channels.Select(c => c.ChannelName).ToArray())); StringBuilder sb = new StringBuilder(); @@ -254,14 +266,14 @@ async Task Capture(CLCaptureOptions opts) { sb.Clear(); - for (int buc = 0; buc < opts.Channels.Length; buc++) + for (int buc = 0; buc < nChannels.Length; buc++) { - if ((result.Samples[sample] & ((UInt128)1 << buc)) == 0) + if ((result.Samples[sample] & ((UInt128)1 << nChannels[buc])) == 0) sb.Append("0,"); else sb.Append("1,"); } - + sb.Remove(sb.Length - 1, 1); sw.WriteLine(sb.ToString()); } diff --git a/Software/LogicAnalyzer/CLCapture/Properties/launchSettings.json b/Software/LogicAnalyzer/CLCapture/Properties/launchSettings.json index f066825..0a33340 100644 --- a/Software/LogicAnalyzer/CLCapture/Properties/launchSettings.json +++ b/Software/LogicAnalyzer/CLCapture/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "CLCapture": { "commandName": "Project", - "commandLineArgs": "capture COM3 1000000 1,2,3,4,5,6,7,8 20 20000 TriggerType:Edge,Channel:1,Value:1 ../test.csv" + "commandLineArgs": "capture COM9 1000000 1:SDA,2:SDB,3:SDC_A,4:nana,5,6,7,8 20 20000 TriggerType:Edge,Channel:1,Value:0 test.csv" } } } \ No newline at end of file diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Classes/AnalysisSettings.cs b/Software/LogicAnalyzer/LogicAnalyzer/Classes/AnalysisSettings.cs new file mode 100644 index 0000000..a2dd9f6 --- /dev/null +++ b/Software/LogicAnalyzer/LogicAnalyzer/Classes/AnalysisSettings.cs @@ -0,0 +1,16 @@ +using LogicAnalyzer.Protocols; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LogicAnalyzer.Classes +{ + public class AnalysisSettings + { + public ProtocolAnalyzerBase? Analyzer { get; set; } + public ProtocolAnalyzerSelectedChannel[]? Channels { get; set; } + public ProtocolAnalyzerSettingValue[]? Settings { get; set; } + } +} diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs index de978be..d1e0e06 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs @@ -142,7 +142,7 @@ namespace LogicAnalyzer.Dialogs private void LoadSettings(AnalyzerDriverType DriverType) { - settingsFile = $"cpSettings{DriverType}.json"; + settingsFile = WindowExtensions.GetFilePath($"cpSettings{DriverType}.json"); if (File.Exists(settingsFile)) { @@ -316,7 +316,13 @@ namespace LogicAnalyzer.Dialogs { trigger = (int)nudTriggerBase.Value - 1; - char[] patternChars = txtPattern.Text.ToArray(); + if (string.IsNullOrWhiteSpace(txtPattern.Text)) + { + await this.ShowError("Error", "Trigger pattern must be at least one bit long."); + return; + } + + char[] patternChars = txtPattern.Text.Trim().ToArray(); if (patternChars.Length == 0) { diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Extensions/WindowExtensions.cs b/Software/LogicAnalyzer/LogicAnalyzer/Extensions/WindowExtensions.cs index d89447e..9b3e600 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Extensions/WindowExtensions.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Extensions/WindowExtensions.cs @@ -11,18 +11,32 @@ using System.Threading.Tasks; using System.IO; using Newtonsoft.Json; using System.Text.Json; +using static System.Environment; namespace LogicAnalyzer.Extensions { public static class WindowExtensions { + + static string BasePath { get { return Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) ?? ""; } } + static JsonSerializerSettings jSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Formatting = Formatting.Indented }; + public static string GetFilePath(string FileName) + { + string appData = Path.Combine(Environment.GetFolderPath(SpecialFolder.ApplicationData, SpecialFolderOption.DoNotVerify), "LogicAnalyzer"); + Directory.CreateDirectory(appData); + return Path.Combine(appData, FileName); + } + static AppConfig config; static WindowExtensions() { - if (File.Exists("AppConfig.json")) - config = JsonConvert.DeserializeObject(File.ReadAllText("AppConfig.json"), jSettings); + + string path = GetFilePath("AppConfig.json"); + + if (File.Exists(path)) + config = JsonConvert.DeserializeObject(File.ReadAllText(path), jSettings); if(config == null) config = new AppConfig(); @@ -132,8 +146,9 @@ namespace LogicAnalyzer.Extensions } private static void PersistSettings() { + string path = GetFilePath("AppConfig.json"); - File.WriteAllText("AppConfig.json", JsonConvert.SerializeObject(config, Formatting.Indented, jSettings)); + File.WriteAllText(path, JsonConvert.SerializeObject(config, Formatting.Indented, jSettings)); } private static object GetPropertyValue(object Source, string PropertyName) { diff --git a/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj b/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj index 6d5f5a5..63a1db0 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj +++ b/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj @@ -8,7 +8,7 @@ true Assets\Ico40.ico True - 4.0.0.0 + 4.5.1.0 diff --git a/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj.user b/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj.user index 58adfad..87f9830 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj.user +++ b/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj.user @@ -1,6 +1,10 @@  - <_LastSelectedProfileId>C:\Users\geniw\source\repos\LogicAnalyzer\LogicAnalyzer\Properties\PublishProfiles\Windows-Arm64.pubxml + <_LastSelectedProfileId>C:\Users\geniw\source\repos\LogicAnalyzer\LogicAnalyzer\Properties\PublishProfiles\Linux.pubxml + LogicAnalyzer + + + ProjectDebugger \ No newline at end of file diff --git a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml index fd19e6e..03b1e5f 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml +++ b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml @@ -3,11 +3,11 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:controls="clr-namespace:LogicAnalyzer.Controls" - mc:Ignorable="d" d:DesignWidth="1024" d:DesignHeight="800" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600" x:Class="LogicAnalyzer.MainWindow" Title="LogicAnalyzer - Multiplatform version" Icon="/Assets/Ico40.ico" - Background="#383838" MinWidth="1024" MinHeight="800" Width="1024" Height="800" WindowStartupLocation="CenterScreen"> + Background="#383838" MinWidth="800" MinHeight="600" Width="800" Height="600" WindowStartupLocation="CenterScreen"> @@ -47,7 +47,7 @@ - + diff --git a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs index 2abce6e..2ed0239 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs @@ -35,6 +35,12 @@ namespace LogicAnalyzer UInt128[]? copiedSamples; + MenuItem? mnuRepeatAnalysis; + + AnalysisSettings? analysisSettings; + + bool preserveSamples = false; + public MainWindow() { Instance = this; @@ -558,7 +564,10 @@ namespace LogicAnalyzer sampleViewer.Samples = e.Samples; sampleViewer.PreSamples = e.PreSamples; sampleViewer.ChannelCount = e.ChannelCount; - sampleViewer.SamplesInScreen = Math.Min(100, e.Samples.Length / 10); + + if(!preserveSamples) + sampleViewer.SamplesInScreen = Math.Min(100, e.Samples.Length / 10); + sampleViewer.FirstSample = Math.Max(e.PreSamples - 10, 0); sampleViewer.ClearRegions(); sampleViewer.ClearAnalyzedChannels(); @@ -613,14 +622,43 @@ namespace LogicAnalyzer return itm; }).ToArray()); + mnuRepeatAnalysis = new MenuItem { Header = "Repeat last analysis" }; + mnuRepeatAnalysis.IsEnabled = false; + mnuRepeatAnalysis.Click += MnuRepeatAnalysis_Click; + finalItems.Add(mnuRepeatAnalysis); + var clearItem = new MenuItem { Header = "C_lear analysis data" }; clearItem.Click += ClearItem_Click; finalItems.Add(clearItem); + + mnuProtocols.Items = finalItems; } } + private void MnuRepeatAnalysis_Click(object? sender, RoutedEventArgs e) + { + + if (analysisSettings == null || analysisSettings.Channels == null || analysisSettings.Analyzer == null || analysisSettings.Settings == null) + return; + + var channels = analysisSettings.Channels; + var samples = sampleViewer.Samples; + + foreach (var channel in channels) + ExtractSamples(channel, samples); + + var analysisResult = analysisSettings.Analyzer.Analyze(settings.Frequency, settings.PreTriggerSamples - 1, analysisSettings.Settings, channels); + + if (analysisResult != null) + { + sampleViewer.BeginUpdate(); + sampleViewer.AddAnalyzedChannels(analysisResult); + sampleViewer.EndUpdate(); + } + } + private void ClearItem_Click(object? sender, RoutedEventArgs e) { sampleViewer.BeginUpdate(); @@ -662,6 +700,11 @@ namespace LogicAnalyzer sampleViewer.AddAnalyzedChannels(analysisResult); sampleViewer.EndUpdate(); } + + analysisSettings = new AnalysisSettings { Analyzer = analyzer, Channels = channels, Settings = dlg.SelectedSettings }; + + if(mnuRepeatAnalysis != null) + mnuRepeatAnalysis.IsEnabled = true; } } @@ -762,7 +805,7 @@ namespace LogicAnalyzer await this.ShowError("Error", "No capture to repeat"); return; } - + preserveSamples = true; BeginCapture(); } @@ -775,6 +818,7 @@ namespace LogicAnalyzer return; settings = dialog.SelectedSettings; + preserveSamples = false; BeginCapture(); } diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Properties/launchSettings.json b/Software/LogicAnalyzer/LogicAnalyzer/Properties/launchSettings.json new file mode 100644 index 0000000..a25c97c --- /dev/null +++ b/Software/LogicAnalyzer/LogicAnalyzer/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "LogicAnalyzer": { + "commandName": "Project" + }, + "WSL": { + "commandName": "WSL2", + "distributionName": "" + } + } +} \ No newline at end of file diff --git a/Wiki artwork/LogoPage.jpg b/Wiki artwork/LogoPage.jpg new file mode 100644 index 0000000..529292f Binary files /dev/null and b/Wiki artwork/LogoPage.jpg differ diff --git a/Wiki artwork/LogoPage.psd b/Wiki artwork/LogoPage.psd new file mode 100644 index 0000000..947686b Binary files /dev/null and b/Wiki artwork/LogoPage.psd differ