diff --git a/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs b/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs index 2526284..4673073 100644 --- a/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs +++ b/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs @@ -32,8 +32,16 @@ public class CLCaptureOptions [Value(6, Required = true, HelpText = "Trigger definition in the form of \"TriggerType:(Edge, Fast or Complex),Channel:(base trigger channel),Value:(string containing 1's and 0's indicating each trigger chanel state)\".")] public CLTrigger? Trigger { get; set; } + [Value(7, Required = true, HelpText = "Name of the output file.")] public string? OutputFile { get; set; } + + [Value(8, Required = false, HelpText = "Format of the output file. Can be csv|vcd or csv:vcd to export both")] + public string? OutputFileFormat { get; set; } + + public bool ExportVCD => OutputFileFormat?.Contains("vcd") ?? false; + + public bool ExportCSV => string.IsNullOrEmpty(OutputFileFormat) || OutputFileFormat.Contains("csv"); } public class CLTrigger diff --git a/Software/LogicAnalyzer/CLCapture/Program.cs b/Software/LogicAnalyzer/CLCapture/Program.cs index 0b8ad7d..b46c4ed 100644 --- a/Software/LogicAnalyzer/CLCapture/Program.cs +++ b/Software/LogicAnalyzer/CLCapture/Program.cs @@ -2,8 +2,6 @@ using CommandLine; using SharedDriver; using System.IO.Ports; -using System.Linq; -using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; @@ -159,6 +157,12 @@ async Task Capture(CLCaptureOptions opts) break; } + if (string.IsNullOrWhiteSpace(opts.OutputFile)) + { + Console.WriteLine("Output file not specified."); + return -1; + } + Console.WriteLine($"Opening logic analyzer in {opts.AddressPort}..."); try @@ -171,7 +175,7 @@ async Task Capture(CLCaptureOptions opts) return -1; } - Console.WriteLine($"Conneced to device {driver.DeviceVersion} in port/address {opts.AddressPort}"); + Console.WriteLine($"Connected to device {driver.DeviceVersion} in port/address {opts.AddressPort}"); captureCompletedTask = new TaskCompletionSource(); @@ -180,7 +184,7 @@ async Task Capture(CLCaptureOptions opts) if (opts.Trigger.TriggerType == CLTriggerType.Edge) { Console.WriteLine("Starting edge triggered capture..."); - var resStart = driver.StartCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples, opts.LoopCount < 2 ? 0 : opts.LoopCount - 1, nChannels, opts.Trigger.Channel - 1, opts.Trigger.Value == "0", CaptureFinished); + var resStart = driver.StartCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples, opts.LoopCount < 2 ? 0 : opts.LoopCount - 1, true, nChannels, opts.Trigger.Channel - 1, opts.Trigger.Value == "0", CaptureFinished); if (resStart != CaptureError.None) { @@ -252,22 +256,108 @@ async Task Capture(CLCaptureOptions opts) return -1; } - Console.WriteLine("Capture complete, writting output file..."); + Console.WriteLine("Capture complete, writing output file..."); + + if (opts.ExportVCD) + ExportVCD(nChannels, result.Samples, result.BurstTimestamps, opts); + + if (opts.ExportCSV) + ExportCSV(channels, nChannels, result.Samples, opts.OutputFile); + + Console.WriteLine("Done."); + + return 1; +} + +void ExportVCD(int[] channelIndices, UInt128[] samples, uint[] burstTimeStamps, CLCaptureOptions opts) +{ + using var vcdFile = File.Create(Path.ChangeExtension(opts.OutputFile, "vcd")); + using var vcdWriter = new StreamWriter(vcdFile); - var file = File.Create(opts.OutputFile); - StreamWriter sw = new StreamWriter(file); + var vcdSb = new StringBuilder(); + + uint timeStep = 1000000000U / (uint)opts.SamplingFrequency; + + var dateNowStr = DateTime.Now.ToString("ddd MMM MM HH:mm:ss yyyy", new System.Globalization.CultureInfo("en-us")); + vcdSb.Append($"$date {dateNowStr} $end\n"); + vcdSb.Append($"$timescale {timeStep} ns $end\n"); + + char channelId = '!'; + var channelAliases = new Dictionary(); + foreach (var channelIdx in channelIndices) + { + vcdSb.Append($"$var wire 1 {channelId} 0 $end\n"); + channelAliases[channelIdx] = channelId; + + channelId++; + } + + vcdSb.Append("$enddefinitions $end\n\n"); + + char getChannelValue(UInt128 sample, int channelIdx) + { + return (sample & ((UInt128)1 << channelIdx)) == 0 ? '0' : '1'; + } + + UInt128? prevSample = null; + void appendSampleIfChanged(uint sampleIdx, UInt128 sample) + { + if (sample == prevSample) + return; + + vcdSb.Append($"#{sampleIdx}"); + + foreach (var channelIdx in channelIndices) + { + var currentChannelValue = getChannelValue(sample, channelIdx); + if (!prevSample.HasValue || currentChannelValue != getChannelValue(prevSample.Value, channelIdx)) + vcdSb.Append($" {currentChannelValue}{channelAliases[channelIdx]}"); + } + + vcdSb.Append('\n'); + } + + uint sampleIdxOffset = 0; // offset for a samples after the first burst, converted to index, since vcd format operates with indices and not time + + for (uint sampleIdx = 0; sampleIdx < samples.Length; sampleIdx++) + { + if (sampleIdx >= opts.PreSamples) + { + var burstStartIdx = (sampleIdx - opts.PreSamples); + + if (burstStartIdx > 0 && burstStartIdx % opts.PostSamples == 0) // first burst does not have an offset, all consequent - does + { + var burstIdx = burstStartIdx / opts.PostSamples - 1; + sampleIdxOffset += burstTimeStamps[burstIdx] / timeStep; + } + } + + UInt128 sample = samples[sampleIdx]; + appendSampleIfChanged(sampleIdx + sampleIdxOffset, sample); + prevSample = sample; + } + + vcdWriter.Write(vcdSb.ToString()); + vcdWriter.Flush(); + vcdFile.Flush(); +} + +void ExportCSV(CLChannel[] channels, int[] channelIndices, UInt128[] samples, string outputFileName) +{ + using var file = File.Create(Path.ChangeExtension(outputFileName, "csv")); + using var sw = new StreamWriter(file); sw.WriteLine(String.Join(',', channels.Select(c => c.ChannelName).ToArray())); - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); - for (int sample = 0; sample < result.Samples.Length; sample++) + for (int sample = 0; sample < samples.Length; sample++) { sb.Clear(); - for (int buc = 0; buc < nChannels.Length; buc++) + for (int buc = 0; buc < channelIndices.Length; buc++) { - if ((result.Samples[sample] & ((UInt128)1 << nChannels[buc])) == 0) + if ((samples[sample] & ((UInt128)1 << channelIndices[buc])) == 0) sb.Append("0,"); else sb.Append("1,"); @@ -276,14 +366,8 @@ async Task Capture(CLCaptureOptions opts) sw.WriteLine(sb.ToString()); } - sw.Close(); - sw.Dispose(); - file.Close(); - file.Dispose(); - - Console.WriteLine("Done."); - - return 1; + sw.Flush(); + file.Flush(); } int Configure(CLNetworkOptions opts) @@ -334,7 +418,7 @@ int Configure(CLNetworkOptions opts) return -1; } - Console.WriteLine($"Conneced to device {driver.DeviceVersion} in port {opts.SerialPort}"); + Console.WriteLine($"Connected to device {driver.DeviceVersion} in port {opts.SerialPort}"); if (driver.DeviceVersion == null || !driver.DeviceVersion.Contains("WIFI")) { diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Classes/BurstInfo.cs b/Software/LogicAnalyzer/LogicAnalyzer/Classes/BurstInfo.cs new file mode 100644 index 0000000..ef38dd9 --- /dev/null +++ b/Software/LogicAnalyzer/LogicAnalyzer/Classes/BurstInfo.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace LogicAnalyzer.Classes +{ + public class BurstInfo + { + public int SampleNumber { get; set; } + + public uint Nanoseconds { get; set; } + + public string GetTime() + { + DefaultInterpolatedStringHandler defaultInterpolatedStringHandler; + if (Nanoseconds < 1000.0) + { + defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(3, 1); + defaultInterpolatedStringHandler.AppendFormatted(Nanoseconds); + defaultInterpolatedStringHandler.AppendLiteral(" ns"); + return defaultInterpolatedStringHandler.ToStringAndClear(); + } + if (Nanoseconds < 1000000.0) + { + double microseconds = Nanoseconds / 1000.0; + defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(3, 1); + defaultInterpolatedStringHandler.AppendFormatted(microseconds, "F3"); + defaultInterpolatedStringHandler.AppendLiteral(" µs"); + return defaultInterpolatedStringHandler.ToStringAndClear(); + } + if (Nanoseconds < 1000000000.0) + { + double milliseconds = Nanoseconds / 1000000.0; + defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(3, 1); + defaultInterpolatedStringHandler.AppendFormatted(milliseconds, "F3"); + defaultInterpolatedStringHandler.AppendLiteral(" ms"); + return defaultInterpolatedStringHandler.ToStringAndClear(); + } + double seconds = Nanoseconds / 1000000000.0; + defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(2, 1); + defaultInterpolatedStringHandler.AppendFormatted(seconds, "F3"); + defaultInterpolatedStringHandler.AppendLiteral(" s"); + return defaultInterpolatedStringHandler.ToStringAndClear(); + } + } +} diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Classes/CaptureSettings.cs b/Software/LogicAnalyzer/LogicAnalyzer/Classes/CaptureSettings.cs index cdc824a..d65c57b 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Classes/CaptureSettings.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Classes/CaptureSettings.cs @@ -13,6 +13,7 @@ public class CaptureSettings public int PreTriggerSamples { get; set; } public int PostTriggerSamples { get; set; } public int LoopCount { get; set; } + public bool MeasureBursts { get; set; } public CaptureChannel[] CaptureChannels { get; set; } = new CaptureChannel[0]; public int TriggerType { get; set; } public int TriggerChannel { get; set; } diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Classes/SampleRegion.cs b/Software/LogicAnalyzer/LogicAnalyzer/Classes/SampleRegion.cs index 59ce390..d37baa5 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Classes/SampleRegion.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Classes/SampleRegion.cs @@ -69,4 +69,11 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer } } } + + public class BurstGapRegion : SampleRegion + { + public int GapSamples { get; init; } + + public int BurstDelaySamples { get; init; } + } } \ No newline at end of file diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml.cs index 0bcdaa8..557b9d9 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml.cs @@ -49,6 +49,17 @@ public int VisibleSamples set { SetValue(BackgroundProperty, value); InvalidateVisual(); } } + private BurstInfo[] bursts; + public BurstInfo[] Bursts + { + get => bursts; + set + { + bursts = value; + InvalidateVisual(); + } + } + public event EventHandler RegionCreated; public event EventHandler RegionDeleted; @@ -73,6 +84,10 @@ public int VisibleSamples bool samplesCopied = false; bool updating = false; + private Color burstPenColor = Color.FromRgb(224, 175, 29); + + private Color burstFillColor = Color.FromArgb(128, 224, 175, 29); + public SampleMarker() { InitializeComponent(); @@ -325,6 +340,32 @@ public override void Render(DrawingContext context) } } + if (this.bursts != null) + { + double burstWidth = 16.0; + foreach (var burstInfo in bursts) + { + double x4 = (burstInfo.SampleNumber - FirstSample) * sampleWidth - burstWidth / 2; + double x5 = (burstInfo.SampleNumber - FirstSample) * sampleWidth + burstWidth / 2; + double y5 = 0.0; + double y6 = Bounds.Height; + var container = new PathFigure + { + StartPoint = new(x4, y5) + }; + container.Segments.Add(new LineSegment { Point = new Point(x5, y5) }); + container.Segments.Add(new LineSegment { Point = new Point(x5, y6 / 2.0) }); + container.Segments.Add(new LineSegment { Point = new Point(x4 + (x5 - x4) / 2.0, y6) }); + container.Segments.Add(new LineSegment { Point = new Point(x4, y6 / 2.0) }); + container.Segments.Add(new LineSegment { Point = new Point(x4, y5 / 2.0) }); + + container.IsClosed = true; + PathGeometry gContainer = new PathGeometry(); + gContainer.Figures.Add(container); + context.DrawGeometry(GraphicObjectsCache.GetBrush(this.burstFillColor), GraphicObjectsCache.GetPen(this.burstPenColor, 1), gContainer); + } + } + base.Render(context); } } @@ -348,6 +389,30 @@ protected override void OnPointerMoved(PointerEventArgs e) } else { + if (bursts != null) + { + var burst = bursts.FirstOrDefault(b => + { + double num = (b.SampleNumber - FirstSample) * sampleWidth; + double minX = num - 8.0; + double maxX = num + 8.0; + return pos.Position.X >= minX && pos.Position.X <= maxX; + }); + if (burst != null) + { + string burstText = "Burst delay: " + burst.GetTime(); + object tip = ToolTip.GetTip(this); + if (((tip != null) ? tip.ToString() : null) != burstText) + { + ToolTip.SetTip(this, burstText); + ToolTip.SetIsOpen(this, false); + ToolTip.SetShowDelay(this, 0); + ToolTip.SetIsOpen(this, true); + } + return; + } + } + if (ToolTip.GetTip(this)?.ToString() != ovrSample.ToString()) { ToolTip.SetTip(this, ovrSample.ToString()); diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs index f594516..dd691d9 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs @@ -1,9 +1,9 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Controls.Shapes; -using Avalonia.Markup.Xaml; +using Avalonia.Input; using Avalonia.Media; using LogicAnalyzer.Classes; +using LogicAnalyzer.Extensions; using LogicAnalyzer.Protocols; using System; using System.Collections.Generic; @@ -15,6 +15,8 @@ public partial class SampleViewer : UserControl { const int MIN_CHANNEL_HEIGHT = 48; + private Point? _lastPointerLocation; + public int PreSamples { get; set; } public int[]? Bursts { get; set; } public UInt128[] Samples { get; set; } @@ -31,6 +33,8 @@ public partial class SampleViewer : UserControl public SampleRegion[] SelectedRegions { get { return regions.ToArray(); } } + public int TimeStepNs { get; internal set; } + List analysisData = new List(); Color sampleLineColor = Color.FromRgb(60, 60, 60); Color sampleDashColor = Color.FromArgb(60, 60, 60, 60); @@ -93,14 +97,13 @@ public void ClearAnalyzedChannels() public override void Render(DrawingContext context) { - int ChannelCount = Channels?.Length ?? 0; int minSize = ChannelCount * MIN_CHANNEL_HEIGHT; if (Parent.Bounds.Height > minSize && this.Height != double.NaN) this.Height = double.NaN; - else if(Bounds.Height < minSize) + else if (Bounds.Height < minSize) this.Height = minSize; base.Render(context); @@ -118,12 +121,12 @@ public override void Render(DrawingContext context) int lastSample = Math.Min(SamplesInScreen + FirstSample, Samples.Length); - + for (int chan = 0; chan < ChannelCount; chan++) { context.FillRectangle(GraphicObjectsCache.GetBrush(AnalyzerColors.BgChannelColors[chan % 2]), new Rect(0, chan * channelHeight, thisBounds.Width, channelHeight)); } - + if (regions.Count > 0) { foreach (var region in regions) @@ -131,10 +134,12 @@ public override void Render(DrawingContext context) int first = Math.Min(region.FirstSample, region.LastSample); double start = (first - FirstSample) * sampleWidth; double end = sampleWidth * region.SampleCount; + context.FillRectangle(GraphicObjectsCache.GetBrush(region.RegionColor), new Rect(start, 0, end, this.Bounds.Height)); } } + var horizontalLineSegments = new Dictionary<(int channel, double lineLevel), List<(double start, double end)>>(); for (int buc = FirstSample; buc < lastSample; buc++) { @@ -164,7 +169,7 @@ public override void Render(DrawingContext context) } } - if(UserMarker != null && UserMarker == buc) + if (UserMarker != null && UserMarker == buc) context.DrawLine(GraphicObjectsCache.GetPen(userLineColor, 2, DashStyle.DashDot), new Point(lineX, 0), new Point(lineX, thisBounds.Height)); for (int chan = 0; chan < ChannelCount; chan++) @@ -178,7 +183,15 @@ public override void Render(DrawingContext context) else lineY = (chan + 1) * channelHeight - margin; - context.DrawLine(GraphicObjectsCache.GetPen(Channels?[chan].ChannelColor ?? AnalyzerColors.FgChannelColors[chan], 2), new Point(lineX, lineY), new Point(lineX + sampleWidth, lineY)); + // render performance penalty due to a lots of small lines being drawn + //context.DrawLine(GraphicObjectsCache.GetPen(Channels?[chan].ChannelColor ?? AnalyzerColors.FgChannelColors[chan], 2), new Point(lineX, lineY), new Point(lineX + sampleWidth, lineY)); + // collect all horizontal lines needed, then merge them to draw one line at once + { + if (!horizontalLineSegments.TryGetValue((chan, lineY), out var lineSegments)) + horizontalLineSegments[(chan, lineY)] = lineSegments = new List<(double start, double end)>(); + + lineSegments.Add((lineX, lineX + sampleWidth)); + } if (curVal != prevVal && buc != 0) { @@ -186,9 +199,10 @@ public override void Render(DrawingContext context) context.DrawLine(GraphicObjectsCache.GetPen(Channels?[chan].ChannelColor ?? AnalyzerColors.FgChannelColors[chan], 2), new Point(lineX, lineY), new Point(lineX, lineY + channelHeight - margin * 2)); } } - } + RenderHorizontalLines(context, horizontalLineSegments); + if (UserMarker != null && UserMarker == lastSample) { double lineX = (lastSample - FirstSample) * sampleWidth; @@ -218,8 +232,145 @@ public override void Render(DrawingContext context) } } } + + if (IsPointerOver && _lastPointerLocation != null) + RenderPointerRuler(context, _lastPointerLocation.Value, sampleWidth, lastSample, channelHeight, margin); + } + } + + private void RenderHorizontalLines(DrawingContext context, Dictionary<(int channel, double lineLevel), List<(double start, double end)>> lines) + { + foreach (var channelLineSegments in lines) + { + var chan = channelLineSegments.Key.channel; + var lineY = channelLineSegments.Key.lineLevel; + + for (int i = 0; i < channelLineSegments.Value.Count;) + { + var seg = channelLineSegments.Value[i]; + var start = seg.start; + var end = seg.end; + var innerIdx = i + 1; + while (innerIdx < channelLineSegments.Value.Count) + { + if (Math.Abs(end - channelLineSegments.Value[innerIdx].start) < 0.001) + { + end = channelLineSegments.Value[innerIdx].end; + innerIdx++; + } + else + { + break; + } + } + + context.DrawLine(GraphicObjectsCache.GetPen(Channels?[chan].ChannelColor ?? AnalyzerColors.FgChannelColors[chan], 2), new Point(start, lineY), new Point(end, lineY)); + i = innerIdx; + } + } + } + + private void RenderPointerRuler(DrawingContext context, Point pointerLocation, double sampleWidth, int lastSample, double channelHeight, double channelMargin) + { + if (Channels == null) + return; + + var sampleIdx = (int)(pointerLocation.X / sampleWidth + FirstSample); + if (sampleIdx >= lastSample || sampleIdx < 0) + return; + + var channelIdx = (int)(pointerLocation.Y / channelHeight); + if (channelIdx >= Channels.Length) + return; + + var channelTopMargin = channelIdx * channelHeight + channelMargin; + var channelBotMargin = (channelIdx + 1) * channelHeight - channelMargin; + if (pointerLocation.Y < channelTopMargin || pointerLocation.Y > channelBotMargin) + return; + + var channelNum = Channels[channelIdx].ChannelNumber; + + UInt128 sample = Samples[sampleIdx]; + UInt128 sampleValue = getChannelValue(sample, channelNum); + + int leftSampleIdx = sampleIdx; + UInt128 leftSampleValue = sampleValue; + do + { + UInt128 leftSample = Samples[leftSampleIdx]; + leftSampleValue = getChannelValue(leftSample, channelNum); + + leftSampleIdx--; + } + while (leftSampleIdx >= 0 && leftSampleValue == sampleValue); + if (leftSampleValue != sampleValue) + leftSampleIdx++; + else + return; + + int rightSampleIdx = sampleIdx; + UInt128 rightSampleValue = sampleValue; + do + { + UInt128 rightSample = Samples[rightSampleIdx]; + rightSampleValue = getChannelValue(rightSample, channelNum); + + rightSampleIdx++; + } + while (rightSampleIdx < Samples.Length && rightSampleValue == sampleValue); + if (rightSampleValue != sampleValue) + rightSampleIdx--; + else + return; + + var lineStart = ((Math.Max(FirstSample, leftSampleIdx) - FirstSample) + 1) * sampleWidth; + var lineEnd = (Math.Min(lastSample, rightSampleIdx) - FirstSample) * sampleWidth; + + const int arrowSize = 5; + + context.DrawLine(GraphicObjectsCache.GetPen(userLineColor, 1, DashStyle.DashDot), new Point(lineStart, pointerLocation.Y), new Point(lineEnd, pointerLocation.Y)); + + if (leftSampleIdx >= FirstSample) + { + context.DrawLine(GraphicObjectsCache.GetPen(userLineColor, 1), new Point(lineStart, pointerLocation.Y), new Point(lineStart + arrowSize, pointerLocation.Y + arrowSize)); + context.DrawLine(GraphicObjectsCache.GetPen(userLineColor, 1), new Point(lineStart, pointerLocation.Y), new Point(lineStart + arrowSize, pointerLocation.Y - arrowSize)); + } + + if (rightSampleIdx <= lastSample) + { + context.DrawLine(GraphicObjectsCache.GetPen(userLineColor, 1), new Point(lineEnd, pointerLocation.Y), new Point(lineEnd - arrowSize, pointerLocation.Y + arrowSize)); + context.DrawLine(GraphicObjectsCache.GetPen(userLineColor, 1), new Point(lineEnd, pointerLocation.Y), new Point(lineEnd - arrowSize, pointerLocation.Y - arrowSize)); } + + var samplesWidth = rightSampleIdx - leftSampleIdx - 1; + + var region = regions.OfType().FirstOrDefault(r => r.FirstSample >= leftSampleIdx && r.LastSample <= rightSampleIdx); + if (region != null) + samplesWidth = samplesWidth - region.GapSamples + region.BurstDelaySamples; + + var timeStr = (samplesWidth * TimeStepNs / 1000000000d).ToSmallTime(); + var formattedText = new FormattedText($"{timeStr} ({samplesWidth} samples)", Typeface.Default, 12, TextAlignment.Center, TextWrapping.NoWrap, Size.Infinity); + context.DrawText(Foreground, new((lineEnd - lineStart) / 2 + lineStart - 20, pointerLocation.Y - 16), formattedText); + } + + static UInt128 getChannelValue(UInt128 sample, int channelNumber) => sample & ((UInt128)1 << channelNumber); + + protected override void OnPointerMoved(PointerEventArgs e) + { + base.OnPointerMoved(e); + + var point = e.GetCurrentPoint(this); + + _lastPointerLocation = IsPointerOver ? point.Position : null; + InvalidateVisual(); } + protected override void OnPointerLeave(PointerEventArgs e) + { + base.OnPointerLeave(e); + + _lastPointerLocation = null; + InvalidateVisual(); + } } } diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml index 2b1fd3a..9d0a128 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml +++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml @@ -163,6 +163,7 @@ Burst mode Burst count: + Measure burst Pattern trigger diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs index 41bb4ee..2fc43d7 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs @@ -172,7 +172,7 @@ private void LoadSettings(AnalyzerDriverType DriverType) ckNegativeTrigger.IsChecked = settings.TriggerInverted; ckBurst.IsChecked = settings.LoopCount > 0; nudBurstCount.Value = settings.LoopCount > 0 ? settings.LoopCount + 1 : 2; - + ckMeasure.IsChecked = new bool?(settings.LoopCount > 0 && settings.MeasureBursts); rbTriggerTypePattern.IsChecked = false; rbTriggerTypeEdge.IsChecked = true; @@ -237,7 +237,7 @@ private async void btnAccept_Click(object? sender, RoutedEventArgs e) int max = driver.GetLimits(channelsToCapture.Select(c => c.ChannelNumber).ToArray()).MaxTotalSamples; int loops = (int)((ckBurst.IsChecked ?? false) ? nudBurstCount.Value - 1 : 0); - + bool measure = ckBurst.IsChecked.GetValueOrDefault() && ckMeasure.IsChecked.GetValueOrDefault(); if (nudPreSamples.Value + (nudPostSamples.Value * (loops + 1)) > max) { await this.ShowError("Error", $"Total samples cannot exceed {max}."); @@ -344,6 +344,7 @@ private async void btnAccept_Click(object? sender, RoutedEventArgs e) settings.PreTriggerSamples = (int)nudPreSamples.Value; settings.PostTriggerSamples = (int)nudPostSamples.Value; settings.LoopCount = loops; + settings.MeasureBursts = measure; settings.TriggerInverted = ckNegativeTrigger.IsChecked == true; settings.CaptureChannels = channelsToCapture.ToArray(); diff --git a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml index 787d363..7fd0699 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml +++ b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml @@ -40,6 +40,7 @@ < None > + Hide gaps diff --git a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs index edda8da..3676a0b 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs @@ -3,9 +3,9 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; -using Avalonia.Shared.PlatformSupport; using Avalonia.Threading; using AvaloniaColorPicker; using AvaloniaEdit.Utils; @@ -14,24 +14,18 @@ using LogicAnalyzer.Dialogs; using LogicAnalyzer.Extensions; using LogicAnalyzer.Protocols; -using MessageBox.Avalonia; -using MessageBox.Avalonia.Enums; using Newtonsoft.Json; using SharedDriver; using System; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.IO; using System.IO.Ports; using System.Linq; -using System.Net.WebSockets; -using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; -using static System.Net.Mime.MediaTypeNames; namespace LogicAnalyzer { @@ -51,6 +45,10 @@ public partial class MainWindow : PersistableWindowBase bool preserveSamples = false; Timer tmrPower; + + private bool _hideGapsCheckState; + private bool _updatingHideGapsCheckState; + public MainWindow() { Instance = this; @@ -88,6 +86,8 @@ public MainWindow() mnuAbout.Click += MnuAbout_Click; AddHandler(InputElement.KeyDownEvent, MainWindow_KeyDown, handledEventsToo: true); + _hideGapsCheckState = hideGapsChk.IsChecked ?? false; + LoadAnalyzers(); RefreshPorts(); @@ -743,68 +743,174 @@ private void MnuExit_Click(object? sender, RoutedEventArgs e) Close(); } + UInt128[] _samplesFromScan; + private int _preSamplesFromScan; + private uint[] _burstTimestampsFromScan; + private void Driver_CaptureCompleted(object? sender, CaptureEventArgs e) { if (e.Samples == null) return; - Dispatcher.UIThread.InvokeAsync(() => - { - sampleViewer.BeginUpdate(); - sampleViewer.Samples = e.Samples; - sampleViewer.PreSamples = e.PreSamples; - sampleViewer.Channels = settings.CaptureChannels; + _samplesFromScan = e.Samples; + _preSamplesFromScan = e.PreSamples; + _burstTimestampsFromScan = e.BurstTimestamps; + + Dispatcher.UIThread.InvokeAsync(LoadScanResults); + } + + private void LoadScanResults() + { + if (_samplesFromScan == null) + return; + + var newSamples = new List(); + + var regions = new List(); + + sampleViewer.BeginUpdate(); + + sampleViewer.Samples = _samplesFromScan.ToArray(); + sampleViewer.PreSamples = _preSamplesFromScan; + sampleViewer.Channels = settings.CaptureChannels; - if(!preserveSamples) - sampleViewer.SamplesInScreen = Math.Min(100, e.Samples.Length / 10); + if (!preserveSamples) + sampleViewer.SamplesInScreen = Math.Min(100, _samplesFromScan.Length / 10); - sampleViewer.FirstSample = Math.Max(e.PreSamples - 10, 0); + sampleViewer.FirstSample = Math.Max(_preSamplesFromScan - 10, 0); + + if (settings.LoopCount > 0) + { + int pos = _preSamplesFromScan; + List bursts = new List(); + + for (int buc = 0; buc < settings.LoopCount; buc++) + { + pos = pos + settings.PostTriggerSamples; + bursts.Add(pos); + } - if (settings.LoopCount > 0) + sampleViewer.Bursts = bursts.ToArray(); + uint[] burstTimestamps = _burstTimestampsFromScan; + if (((burstTimestamps != null) ? burstTimestamps.Length : 0) == bursts.Count) { - int pos = e.PreSamples; - List bursts = new List(); + List burstsInfo = new List(); + for (int buc2 = 0; buc2 < burstTimestamps.Length; buc2++) + { + burstsInfo.Add(new BurstInfo + { + SampleNumber = bursts[buc2], + Nanoseconds = _burstTimestampsFromScan[buc2] + }); + } - for (int buc = 0; buc < settings.LoopCount; buc++) { - pos = pos + settings.PostTriggerSamples; - bursts.Add(pos); + uint timeStepNs = 1000000000U / (uint)settings.Frequency; + sampleViewer.TimeStepNs = (int)timeStepNs; + + var samples = _samplesFromScan; + var burstsOffset = 0; + + UInt128 prevSample = 0; + for (uint sampleIdx = 0; sampleIdx < samples.Length; sampleIdx++) + { + if (sampleIdx >= settings.PreTriggerSamples) + { + var burstStartIdx = (sampleIdx - settings.PreTriggerSamples); + + if (burstStartIdx > 0 && burstStartIdx % settings.PostTriggerSamples == 0) // first burst does not have an offset, all consequent - does + { + var burstIdx = burstStartIdx / settings.PostTriggerSamples - 1; + var pointsToInsert = (int)(_burstTimestampsFromScan[burstIdx] / timeStepNs); + + // expand small gaps silently + if (pointsToInsert <= 4) + { + burstsOffset += pointsToInsert; + + for (var j = 0; j < pointsToInsert; ++j) + newSamples.Add(prevSample); + } + else + { + if (_hideGapsCheckState) + pointsToInsert = Math.Min(settings.PostTriggerSamples / 4, pointsToInsert); + + burstsOffset += pointsToInsert; + + for (var j = 0; j < pointsToInsert; ++j) + newSamples.Add(prevSample); + + if (_hideGapsCheckState) + { + regions.Add(new BurstGapRegion() + { + FirstSample = (int)(sampleIdx + burstsOffset - pointsToInsert - 5), + LastSample = (int)(sampleIdx + burstsOffset - 5), + RegionColor = Color.FromArgb(40, 120, 120, 120), + RegionName = $"Burst distance: {(burstsInfo[(int)burstIdx].Nanoseconds / 1000000000d).ToSmallTime()}", + GapSamples = pointsToInsert, + BurstDelaySamples = (int)(burstsInfo[(int)burstIdx].Nanoseconds / timeStepNs), + }); + } + } + + burstsInfo[(int)burstIdx].SampleNumber = (int)(sampleIdx + burstsOffset); + sampleViewer.Bursts[(int)burstIdx] = (int)(sampleIdx + burstsOffset); + } + } + + + prevSample = samples[sampleIdx]; + newSamples.Add(prevSample); + } + + sampleViewer.Samples = newSamples.ToArray(); } - sampleViewer.Bursts = bursts.ToArray(); + sampleMarker.Bursts = burstsInfo.ToArray(); } else - sampleViewer.Bursts = null; + { + sampleMarker.Bursts = null; + } + } + else + sampleViewer.Bursts = null; - sampleViewer.ClearRegions(); - sampleViewer.ClearAnalyzedChannels(); - sampleViewer.EndUpdate(); + if (!preserveSamples) + sampleViewer.SamplesInScreen = Math.Min(100, newSamples.Count / 10); - channelViewer.Channels = settings.CaptureChannels; + sampleViewer.ClearRegions(); + sampleViewer.AddRegions(regions); - samplePreviewer.UpdateSamples(channelViewer.Channels, e.Samples); - samplePreviewer.ViewPosition = sampleViewer.FirstSample; + sampleViewer.ClearAnalyzedChannels(); + sampleViewer.EndUpdate(); - scrSamplePos.Maximum = e.Samples.Length - 1; - scrSamplePos.Value = sampleViewer.FirstSample; - tkInScreen.Value = sampleViewer.SamplesInScreen; + channelViewer.Channels = settings.CaptureChannels; - sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen; - sampleMarker.FirstSample = sampleViewer.FirstSample; - sampleMarker.ClearRegions(); - sampleMarker.ClearAnalyzedChannels(); + samplePreviewer.UpdateSamples(channelViewer.Channels, newSamples.ToArray()); + samplePreviewer.ViewPosition = sampleViewer.FirstSample; - btnCapture.IsEnabled = true; - btnRepeat.IsEnabled = true; - btnOpenClose.IsEnabled = true; - btnAbort.IsEnabled = false; - mnuProtocols.IsEnabled = true; - mnuSave.IsEnabled = true; - mnuExport.IsEnabled = true; - mnuSettings.IsEnabled = driver.DriverType == AnalyzerDriverType.Serial && (driver.DeviceVersion?.Contains("WIFI") ?? false); - LoadInfo(); - GetPowerStatus(); - }); + scrSamplePos.Maximum = newSamples.Count - 1; + scrSamplePos.Value = sampleViewer.FirstSample; + tkInScreen.Value = sampleViewer.SamplesInScreen; + + sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen; + sampleMarker.FirstSample = sampleViewer.FirstSample; + sampleMarker.ClearRegions(); + sampleMarker.ClearAnalyzedChannels(); + + btnCapture.IsEnabled = true; + btnRepeat.IsEnabled = true; + btnOpenClose.IsEnabled = true; + btnAbort.IsEnabled = false; + mnuProtocols.IsEnabled = true; + mnuSave.IsEnabled = true; + mnuExport.IsEnabled = true; + mnuSettings.IsEnabled = driver.DriverType == AnalyzerDriverType.Serial && (driver.DeviceVersion?.Contains("WIFI") ?? false); + LoadInfo(); + GetPowerStatus(); } private void Form1_Load(object sender, EventArgs e) @@ -1146,7 +1252,7 @@ private async void BeginCapture() } else { - var error = driver.StartCapture(settings.Frequency, settings.PreTriggerSamples, settings.PostTriggerSamples, settings.LoopCount, settings.CaptureChannels.Select(c => c.ChannelNumber).ToArray(), settings.TriggerChannel, settings.TriggerInverted); + var error = driver.StartCapture(settings.Frequency, settings.PreTriggerSamples, settings.PostTriggerSamples, settings.LoopCount, settings.MeasureBursts, settings.CaptureChannels.Select(c => c.ChannelNumber).ToArray(), settings.TriggerChannel, settings.TriggerInverted); if (error != CaptureError.None) { @@ -1400,5 +1506,36 @@ private void sampleMarker_RegionDeleted(object? sender, RegionEventArgs e) sampleViewer.RemoveRegion(e.Region); sampleViewer.EndUpdate(); } + + private async void HideGaps_Checked(object? sender, Avalonia.Interactivity.RoutedEventArgs e) => await HideGaps_CheckChanged(); + + private async void HideGaps_Unchecked(object? sender, Avalonia.Interactivity.RoutedEventArgs e) => await HideGaps_CheckChanged(); + + private async Task HideGaps_CheckChanged() + { + if (_updatingHideGapsCheckState) + return; + + if (hideGapsChk.IsChecked == false && _hideGapsCheckState) + { + var res = await this.ShowConfirm("Confirm", "Expanding gaps may generate a large amount of the samples if bursts delay are large. Continue?"); + if (!res) + { + _updatingHideGapsCheckState = true; + try + { + hideGapsChk.IsChecked = true; + return; + } + finally + { + _updatingHideGapsCheckState = false; + } + } + } + + _hideGapsCheckState = hideGapsChk.IsChecked ?? false; + LoadScanResults(); + } } } diff --git a/Software/LogicAnalyzer/SharedDriver/EmulatedAnalizerDriver.cs b/Software/LogicAnalyzer/SharedDriver/EmulatedAnalizerDriver.cs index 6f33ac7..40e9680 100644 --- a/Software/LogicAnalyzer/SharedDriver/EmulatedAnalizerDriver.cs +++ b/Software/LogicAnalyzer/SharedDriver/EmulatedAnalizerDriver.cs @@ -65,7 +65,7 @@ public bool SendNetworkConfig(string AccesPointName, string Password, string IPA throw new NotSupportedException(); } - public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null) + public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, bool MeasureBursts, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null) { throw new NotSupportedException(); } diff --git a/Software/LogicAnalyzer/SharedDriver/IAnalizerDriver.cs b/Software/LogicAnalyzer/SharedDriver/IAnalizerDriver.cs index 3983edc..7538166 100644 --- a/Software/LogicAnalyzer/SharedDriver/IAnalizerDriver.cs +++ b/Software/LogicAnalyzer/SharedDriver/IAnalizerDriver.cs @@ -15,7 +15,7 @@ public interface IAnalizerDriver : IDisposable public int Channels { get; } public event EventHandler CaptureCompleted; public bool SendNetworkConfig(string AccesPointName, string Password, string IPAddress, ushort Port); - public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null); + public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, bool MeasureBursts, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null); public CaptureError StartPatternCapture(int Frequency, int PreSamples, int PostSamples, int[] Channels, int TriggerChannel, int TriggerBitCount, UInt16 TriggerPattern, bool Fast, Action? CaptureCompletedHandler = null); public bool StopCapture(); public CaptureLimits GetLimits(int[] Channels); @@ -30,6 +30,9 @@ public class CaptureEventArgs : EventArgs public int ChannelCount { get; set; } public int PreSamples { get; set; } public UInt128[] Samples { get; set; } + public int LoopCount { get; set; } + + public uint[] BurstTimestamps { get; set; } } public enum AnalyzerDriverType diff --git a/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs b/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs index 5c051a6..588321b 100644 --- a/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs +++ b/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs @@ -12,7 +12,7 @@ namespace SharedDriver public class LogicAnalyzerDriver : IDisposable, IAnalizerDriver { const int MAJOR_VERSION = 5; - const int MINOR_VERSION = 1; + const int MINOR_VERSION = 2; Regex regVersion = new Regex(".*?(V([0-9]+)_([0-9]+))$"); @@ -37,6 +37,10 @@ public class LogicAnalyzerDriver : IDisposable, IAnalizerDriver private int channelCount; private int triggerChannel; private int preSamples; + private int postSamples; + private int loopCount; + private bool measure; + private int frequency; private Action? currentCaptureHandler; bool isNetwork; @@ -184,7 +188,7 @@ public unsafe bool SendNetworkConfig(string AccesPointName, string Password, str return false; } - public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null) + public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, bool MeasureBursts, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null) { if (capturing) @@ -199,7 +203,8 @@ public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, PreSamples < 2 || PostSamples < 512 || Frequency < 3100 || - Frequency > 100000000 + Frequency > 100000000 || + LoopCount > 255 ) return CaptureError.BadParams; @@ -233,6 +238,10 @@ public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, channelCount = Channels.Length; triggerChannel = TriggerChannel; preSamples = PreSamples; + postSamples = PostSamples; + loopCount = LoopCount; + measure = MeasureBursts; + frequency = Frequency; currentCaptureHandler = CaptureCompletedHandler; CaptureRequest request = new CaptureRequest @@ -246,6 +255,7 @@ public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, preSamples = (uint)PreSamples, postSamples = (uint)PostSamples, loopCount = (byte)LoopCount, + measure = MeasureBursts ? (byte)1 : (byte)0, captureMode = captureMode }; @@ -310,6 +320,8 @@ public CaptureError StartPatternCapture(int Frequency, int PreSamples, int PostS channelCount = Channels.Length; triggerChannel = TriggerChannel; preSamples = PreSamples; + loopCount = 0; + measure = false; currentCaptureHandler = CaptureCompletedHandler; CaptureRequest request = new CaptureRequest @@ -362,6 +374,7 @@ private void ReadCapture(int Samples, byte Mode) { uint length = readData.ReadUInt32(); UInt128[] samples = new UInt128[length]; + uint[] timestamps = new uint[(this.loopCount == 0 || !this.measure) ? 0 : (this.loopCount + 2)]; BinaryReader rdData; @@ -369,7 +382,16 @@ private void ReadCapture(int Samples, byte Mode) rdData = readData; else { - byte[] readBuffer = new byte[Samples * (Mode == 0 ? 1 : (Mode == 1 ? 2 : 4))]; + int bufLen = Samples * ((Mode == 0) ? 1 : ((Mode == 1) ? 2 : 4)); + if (this.loopCount == 0 || !this.measure) + { + bufLen++; + } + else + { + bufLen += 1 + (this.loopCount + 2) * 4; + } + byte[] readBuffer = new byte[bufLen]; int left = readBuffer.Length; int pos = 0; @@ -398,11 +420,48 @@ private void ReadCapture(int Samples, byte Mode) samples[buc] = rdData.ReadUInt32(); break; } - + if (rdData.ReadByte() > 0) + { + for (int buc4 = 0; buc4 < this.loopCount + 2; buc4++) + { + timestamps[buc4] = rdData.ReadUInt32(); + } + } + if (timestamps.Length != 0) + { + for (int buc5 = 0; buc5 < timestamps.Length; buc5++) + { + uint tt = timestamps[buc5]; + tt = (tt & 4278190080U) | (16777215U - (tt & 16777215U)); + timestamps[buc5] = tt; + } + double num = 1000000000.0 / (double)this.frequency; + double ticksPerSample = num / 5.0; + double ticksPerBurst = num * (double)this.postSamples / 5.0; + for (int buc6 = 1; buc6 < timestamps.Length; buc6++) + { + ulong top = (ulong)((timestamps[buc6] < timestamps[buc6 - 1]) ? (timestamps[buc6] + uint.MaxValue) : timestamps[buc6]); + if (top - (ulong)timestamps[buc6 - 1] <= ticksPerBurst) + { + uint diff = (uint)(ticksPerBurst - (top - (ulong)timestamps[buc6 - 1]) + ticksPerSample * 2.0); + for (int buc7 = buc6; buc7 < timestamps.Length; buc7++) + { + timestamps[buc7] += diff; + } + } + } + uint[] delays = new uint[timestamps.Length - 2]; + for (int buc8 = 2; buc8 < timestamps.Length; buc8++) + { + ulong top2 = (ulong)((timestamps[buc8] < timestamps[buc8 - 1]) ? (timestamps[buc8] + uint.MaxValue) : timestamps[buc8]); + delays[buc8 - 2] = (uint)((top2 - (ulong)timestamps[buc8 - 1] - ticksPerBurst) * 5.0); + } + timestamps = delays; + } if (currentCaptureHandler != null) - currentCaptureHandler(new CaptureEventArgs { SourceType = isNetwork ? AnalyzerDriverType.Network : AnalyzerDriverType.Serial, Samples = samples, ChannelCount = channelCount, TriggerChannel = triggerChannel, PreSamples = preSamples }); + currentCaptureHandler(new CaptureEventArgs { SourceType = isNetwork ? AnalyzerDriverType.Network : AnalyzerDriverType.Serial, Samples = samples, ChannelCount = channelCount, TriggerChannel = triggerChannel, PreSamples = preSamples, LoopCount = loopCount, BurstTimestamps = timestamps }); else if (CaptureCompleted != null) - CaptureCompleted(this, new CaptureEventArgs { SourceType = isNetwork ? AnalyzerDriverType.Network : AnalyzerDriverType.Serial, Samples = samples, ChannelCount = channelCount, TriggerChannel = triggerChannel, PreSamples = preSamples }); + CaptureCompleted(this, new CaptureEventArgs { SourceType = isNetwork ? AnalyzerDriverType.Network : AnalyzerDriverType.Serial, Samples = samples, ChannelCount = channelCount, TriggerChannel = triggerChannel, PreSamples = preSamples, LoopCount = loopCount, BurstTimestamps = timestamps }); if (!isNetwork) { @@ -505,8 +564,8 @@ public void Dispose() try { - tcpClient.Close(); - tcpClient.Dispose(); + tcpClient?.Close(); + tcpClient?.Dispose(); } catch { } @@ -614,6 +673,7 @@ struct CaptureRequest public UInt32 preSamples; public UInt32 postSamples; public byte loopCount; + public byte measure; public byte captureMode; } @@ -624,6 +684,6 @@ unsafe struct NetConfig public fixed byte Password[64]; public fixed byte IPAddress[16]; public UInt16 Port; - } + } } } \ No newline at end of file diff --git a/Software/LogicAnalyzer/SharedDriver/MultiAnalizerDriver.cs b/Software/LogicAnalyzer/SharedDriver/MultiAnalizerDriver.cs index cc8e272..edb8fa6 100644 --- a/Software/LogicAnalyzer/SharedDriver/MultiAnalizerDriver.cs +++ b/Software/LogicAnalyzer/SharedDriver/MultiAnalizerDriver.cs @@ -117,7 +117,7 @@ public bool SendNetworkConfig(string AccesPointName, string Password, string IPA { throw new NotSupportedException(); } - public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null) + public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, bool MeasureBursts, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null) { throw new NotSupportedException(); } @@ -184,7 +184,7 @@ public CaptureError StartPatternCapture(int Frequency, int PreSamples, int PostS continue; connectedDevices[buc].Tag = channelsCapturing; - var err = connectedDevices[buc].StartCapture(Frequency, PreSamples + offset, PostSamples - offset, 0, chan, 24, false); + var err = connectedDevices[buc].StartCapture(Frequency, PreSamples + offset, PostSamples - offset, 0, false, chan, 24, false); if (err != CaptureError.None) {