void acqEngine_OverviewWaveform(Waveform f) { displayOverview.horizontalScale = f.Horizontal[f.Samples - 1] / 10; displayOverview.horizontalOffset = displayOverview.horizontalScale * displayOverview.horizontalDivisions; displayOverview.Add(f); // Update dmm: float currentMin = float.MaxValue; float currentMax = float.MinValue; float currentRms = 0; float currentMean = 0; for (int k = 0; k < f.Samples; k++) { var c = f.Data[0][k]; currentMin = Math.Min(currentMin, c); currentMax = Math.Max(currentMax, c); currentRms += c * c; currentMean += c; } currentMean /= f.Samples; currentRms *= f.Horizontal[1] - f.Horizontal[0]; currentRms /= f.Horizontal[f.Samples - 1]; currentRms = (float)Math.Sqrt(currentRms); dmm.meanCurrent = currentMean; dmm.rmsCurrent = currentRms; dmm.minCurrent = currentMin; dmm.maxCurrent = currentMax; dmm.sixDigitVoltage = acqEngine.LastVoltageMeasurent; overviewRefresh = true; }
void acqEngine_Waveform(Waveform f) { displayTrig.Add(f); triggerOk = true; }
void acqEngine_OverviewWaveform(Waveform f) { displayOverview.horizontalScale = f.Horizontal[f.Samples - 1]/10; displayOverview.horizontalOffset = displayOverview.horizontalScale * displayOverview.horizontalDivisions; displayOverview.Add(f); // Update dmm: float currentMin = float.MaxValue; float currentMax = float.MinValue; float currentRms = 0; float currentMean = 0; for(int k = 0; k < f.Samples;k ++) { var c = f.Data[0][k]; currentMin = Math.Min(currentMin, c); currentMax = Math.Max(currentMax, c); currentRms += c*c; currentMean += c; } currentMean /= f.Samples; currentRms *= f.Horizontal[1] - f.Horizontal[0]; currentRms /= f.Horizontal[f.Samples - 1]; currentRms = (float)Math.Sqrt(currentRms); dmm.meanCurrent = currentMean; dmm.rmsCurrent = currentRms; dmm.minCurrent = currentMin; dmm.maxCurrent = currentMax; dmm.sixDigitVoltage = acqEngine.LastVoltageMeasurent; overviewRefresh = true; }
public void Add(Waveform waveform) { waveforms.Enqueue(waveform); }
protected override void OnPaint(PaintEventArgs e) { if (waveforms.Count == 0) { return; } base.OnPaint(e); Stopwatch sw = new Stopwatch(); sw.Start(); var w = e.ClipRectangle.Width; var h = e.ClipRectangle.Height; if (w < 1) { w = 1; } var graph = new Bitmap(w, h, PixelFormat.Format32bppArgb); var g = Graphics.FromImage(graph); g.CompositingQuality = CompositingQuality.HighSpeed; g.CompositingMode = CompositingMode.SourceCopy; //g.FillRectangle(Brushes.Black, e.ClipRectangle); var pxPerHorizontalDivision = w / 2 / horizontalDivisions; var offsetHorizontalDivision = w / 2 - (pxPerHorizontalDivision * horizontalDivisions); var pxPerVerticalDivision = h / 2 / verticalDivisions; var offsetVerticalDivision = h / 2 - (pxPerVerticalDivision * verticalDivisions); // Draw grid for (int div = -horizontalDivisions; div <= horizontalDivisions; div++) { var x = offsetHorizontalDivision + pxPerHorizontalDivision * (horizontalDivisions + div); var y = offsetVerticalDivision + pxPerVerticalDivision * verticalDivisions * 2; g.DrawLine((div == 0) ? gridCenterPen : gridPen, x, offsetVerticalDivision, x, y); } for (int div = -verticalDivisions; div <= verticalDivisions; div++) { var x = offsetHorizontalDivision + pxPerHorizontalDivision * horizontalDivisions * 2; var y = offsetVerticalDivision + pxPerVerticalDivision * (verticalDivisions + div); g.DrawLine((div == 0) ? gridCenterPen : gridPen, offsetHorizontalDivision, y, x, y); } var graphWidth = w - offsetHorizontalDivision * 2; var graphHeight = h - offsetVerticalDivision * 2; if (overhead < 0) { overhead = sw.ElapsedMilliseconds; } else { overhead = overhead / 2.0f + sw.ElapsedMilliseconds / 2.0f; } List <Waveform> myWaveforms = new List <Waveform>(); lock (waveformsMutex) { // myWaveforms = new List<Waveform>(waveforms); Waveform f = default(Waveform); while (waveforms.TryDequeue(out f)) { myWaveforms.Add(f); } //if (myWaveforms.Count >= 500) // myWaveforms.RemoveRange(0, myWaveforms.Count - 501); } var compressionRatio = 1; try { // Display all waveforms // To do that, we map all waveforms to an displayTrig array // This array contains the X,Y intensity data per channel // intensity[channel][x position][y position] = number of hits // The number of hits will later determine which pencil is used for drawing. // Which in term makes the specific pixel brighter or dimmer. int[][][] intensity = new int[channels][][]; for (var ch = 0; ch < channels; ch++) { var arr = new int[w][]; for (var x = 0; x < w; x++) { arr[x] = new int[h]; } intensity[ch] = arr; } foreach (var wave in myWaveforms) { // Process may interpolate the waveform if not enough samples are available. // The draw engine is only capable of doing dots mode. if (DotsOnly) { wave.Process(w); } // The compression ratio is applicable when there are more samples to displayTrig than displayTrig width is available. // This will mean that it's possible more than 1 sample is displayed on the same X position. // This can increase the number of hits on that specific pixel, and therefor an compression ratio is used. // It's a compromise between detail & accuracy. Higher resolution = better accuracy, at all times. // But also slower to draw. compressionRatio = Math.Max(wave.Samples / w, compressionRatio); for (var ch = 0; ch < channels; ch++) { var lastX = -1; var lastY = -1; var sameX = 0; int yCenter = (int)((verticalDivisions - verticalOffset[ch]) * pxPerVerticalDivision); for (int s = 0; s < wave.Samples; s++) { var waveTime = wave.Horizontal[s] - wave.TriggerTime; // Calculate X position on screen. Check in bounds. var x = (int) Math.Round(((waveTime - horizontalOffset) / horizontalScale + horizontalDivisions) * pxPerHorizontalDivision); if (x < 0) { continue; } if (x == graphWidth) { x = graphWidth - 1; } if (x > graphWidth) { break; } // Calculate Y position on screen. Check in bounds. var y = (int)((wave.Data[ch][s] / -verticalScale[ch]) * pxPerVerticalDivision) + yCenter; if (y < 0) { y = 0; } if (y > graphHeight) { y = graphHeight - 1; } // Make a hit for this pixel. if (lastX >= 0 && !DotsOnly) { var dx = x - lastX; var dy = y - lastY; if (Math.Abs(dx) > Math.Abs(dy)) { if (dx > 0) { for (var xInterpolated = 0; xInterpolated < dx; xInterpolated++) { var yInterpolated = y - dy * xInterpolated / dx; intensity[ch][x - xInterpolated][yInterpolated]++; } } else { for (var xInterpolated = 0; xInterpolated < -dx; xInterpolated++) { var yInterpolated = y + dy * xInterpolated / dx; intensity[ch][x - xInterpolated][yInterpolated]++; } } } else { if (dy > 0) { for (var yInterpolated = 0; yInterpolated < dy; yInterpolated++) { var xInterpolated = lastX + dx * yInterpolated / dy; try { intensity[ch][xInterpolated][lastY + yInterpolated]++; } catch { } } } else { for (var yInterpolated = 0; yInterpolated < -dy; yInterpolated++) { var xInterpolated = lastX + dx * yInterpolated / -dy; try { var y_ = Math.Min(graphHeight - 1, lastY - yInterpolated); intensity[ch][xInterpolated][y_]++; } catch { } } } } if (lastX != x) { sameX = 2; } else { sameX++; } //compressionRatio = Math.Max(sameX, compressionRatio); } else { intensity[ch][x][y]++; } lastX = x; lastY = y; } } } //compressionRatio = 1; // Lock bits on map // This is used for a much much faster setpixel performance. The bitmap is accesisable via a raw byte arrya, containing bit information. // The format of this array depends on the pixelformat, which is 32-bit R-G-B-A. // Alpha seems to be broken, so we modulate the alpha via the pencil. var dat = graph.LockBits(e.ClipRectangle, ImageLockMode.ReadWrite, graph.PixelFormat); var ptr = dat.Scan0; var bytesPerPixel = 4; byte[] bitmapBuffer = new byte[w * h * bytesPerPixel]; Marshal.Copy(ptr, bitmapBuffer, 0, bitmapBuffer.Length); // From here, we must edit our image in the bitmapBuffer, intead via the bitmap normal API routines. var k = 0; // We take the hit map foreach (var channel in intensity) { var chColor = channelColors[k++]; var noOfPens = myWaveforms.Count * compressionRatio + 1; var penPallette = new byte[noOfPens][]; // Generate a set of color. for (int i = 0; i < noOfPens; i++) { // The accurateness and contrast of the intensity graded displayTrig can be changed here. // With the SQRT less-freuqent signals are "amplified" and more frequent signals are compressed. // The offset will also determine how visible less frequent options are seen. var perc = 0.0f; if (lowContrast) { perc = (float)Math.Pow(i * 1.0f / compressionRatio, 0.5) * 100.0f + 5.0f; } else { perc = (float)Math.Pow(i * 1.0f / noOfPens / compressionRatio, 0.5) * 100.0f + 10.0f; } // Fix perc if <0% or >100% or "ERR" if (perc >= 100) { perc = 100; } if (perc <= 0) { perc = 0; } if (float.IsNaN(perc) || float.IsInfinity(perc)) { perc = 100; } // Colors are saved raw too, because this is faster to access. var c = new byte[4] { (byte)(chColor[2] * perc / 100), (byte)(chColor[1] * perc / 100), (byte)(chColor[0] * perc / 100), 255 }; penPallette[i] = c; } // Walk through the entire "image" for (var x = 0; x < w; x++) { for (var y = 0; y < h; y++) { var chVal = (int)(channel[x][y]); if (chVal > 0) { // We only modify when there was a hit here. // i contains the index inside the bitmapBuffer we must edit. var i = (y + offsetVerticalDivision) * w + x + offsetHorizontalDivision; i *= bytesPerPixel; // We pick a color if (chVal >= noOfPens) { chVal = noOfPens - 1; } var c = penPallette[chVal]; // And copy it. bitmapBuffer[i] = c[0]; // blue bitmapBuffer[i + 1] = c[1]; // green bitmapBuffer[i + 2] = c[2]; // red bitmapBuffer[i + 3] = c[3]; // alpha } } } } Marshal.Copy(bitmapBuffer, 0, ptr, bitmapBuffer.Length); graph.UnlockBits(dat); e.Graphics.DrawImage(graph, 0, 0); } catch { } sw.Stop(); waveformsCount += myWaveforms.Count; renderTime += (float)sw.ElapsedMilliseconds; var dt = DateTime.Now.Subtract(lastWfmsMeasurement); if (dt.TotalMilliseconds > 500 && waveformsCount > 10) { var wfms = waveformsCount / (dt.TotalMilliseconds / 1000.0f); lastMeasurement = wfms.ToString("0000.0 wfms") + " [" + renderTime + "/" + dt.TotalMilliseconds.ToString("000") + "ms] [" + Math.Round(myWaveforms[0].Samples * wfms) + "sps]"; lastMeasurementColor = renderTime >= dt.TotalMilliseconds - 10 ? Brushes.Red : renderTime * 1.2 >= dt.TotalMilliseconds ? Brushes.Orange : Brushes.White; waveformsCount = 0; lastWfmsMeasurement = DateTime.Now; renderTime = 0; } e.Graphics.DrawString(lastMeasurement, new Font("Verdana", 7), lastMeasurementColor, 0, 0); if (speed < 0) { speed = sw.ElapsedMilliseconds; } else { speed = speed * 0.6f + sw.ElapsedMilliseconds * 0.4f; } measurements++; }