Beginnen wir durch die unterschiedlichen Signalarten zu schaffen, ist dies ein Funktion, die eine Wellenlänge der Amplitude 1 erzeugt:
private PointF[] CreateBaseSignal(SignalType signalType)
{
switch (signalType)
{
case SignalType.Sine:
const int oversampling = 32;
PointF[] signal = new PointF[oversampling];
for (int i = 0; i < signal.Length; i++)
{
signal[i].X = (float) i/oversampling;
signal[i].Y = Convert.ToSingle(Math.Sin((double) i/oversampling * 2 * Math.PI));
}
return signal;
case SignalType.Square:
return new PointF[]
{
new PointF(0.0f, -1.0f),
new PointF(0.5f, -1.0f),
new PointF(0.5f, 1.0f),
new PointF(1.0f, 1.0f),
};
case SignalType.Triangle:
return new PointF[]
{
new PointF(0.0f, -1.0f),
new PointF(0.5f, 1.0f),
};
case SignalType.Sawtooth:
return new PointF[]
{
new PointF(0.0f, -1.0f),
new PointF(1.0f, 1.0f),
};
default:
throw new ArgumentException("Invalid signal type", "signalType");
}
}
Dann erstellen wir das tatsächliche Signal mit der gewählten Amplitude und Frequenz:
private PointF[] CreateSignal(PointF[] baseSignal, float frequency, float amplitude)
{
PointF[] signal = new PointF[Convert.ToInt32(Math.Ceiling(baseSignal.Length * frequency))];
for(int i = 0; i < signal.Length; i++)
{
signal[i].X = baseSignal[i % baseSignal.Length].X/frequency + (i/baseSignal.Length)/frequency;
signal[i].Y = baseSignal[i % baseSignal.Length].Y * amplitude;
}
return signal;
}
Bevor Sie versuchen, dieses Signal zu einem PictureBox plotten, skalieren wir das Signal, welches die Breite und Höhe passen:
private PointF[] ScaleSignal(PointF[] signal, int width, int height)
{
const float maximumAmplitude = 10.0f;
PointF[] scaledSignal = new PointF[signal.Length];
for(int i = 0; i < signal.Length; i++)
{
scaledSignal[i].X = signal[i].X * width;
scaledSignal[i].Y = signal[i].Y * height/2/maximumAmplitude;
}
return scaledSignal;
}
Graphics.DrawLine Unter Verwendung des Signals zu zeichnen ist viel besser als Bitmap.SetPixel, da die Daten Punkte werden auch bei hohen Frequenzen verbunden sein. Bitmap.SetPixel ist auch sehr langsam, Sie müssen wirklich Bitmap.LockBits und unsicheren Code für die Manipulation einzelner Pixel verwenden, um eine anständige Leistung zu erzielen. Mit Graphics.DrawLine, können Sie auch die Kontrolle über die Linienbreite haben, Anti-Aliasing usw.
Da wir das Signal in einem PointF Array gespeichert haben, können wir die einfachen Grafiken verwenden.DrawLines Verfahren das Signal statt Iteration über die Datenpunkte zeichnen:
private void PlotSignal(PointF[] signal, PictureBox pictureBox)
{
Bitmap bmp = new Bitmap(pictureBox.ClientSize.Width, pictureBox.ClientSize.Height);
signal = ScaleSignal(signal, bmp.Width, bmp.Height); // Scale signal to fit image
using(Graphics gfx = Graphics.FromImage(bmp))
{
gfx.SmoothingMode = SmoothingMode.HighQuality;
gfx.TranslateTransform(0, bmp.Height/2); // Move Y=0 to center of image
gfx.ScaleTransform(1, -1); // Make positive Y axis point upward
gfx.DrawLine(Pens.Black, 0, 0, bmp.Width, 0); // Draw zero axis
gfx.DrawLines(Pens.Blue, signal); // Draw signal
}
// Make sure the bitmap is disposed the next time around
Image old = pictureBox.Image;
pictureBox.Image = bmp;
if(old != null)
old.Dispose();
}
Wenn Sie das Signal oft neu zu zeichnen, haben Sie wahrscheinlich die die Bitmap und Grafikobjekten statt neue zu schaffen jedes Mal wieder verwenden wollen. Denken Sie daran, Graphics.Clear zwischen jedem Neuzeichnen aufzurufen.
Putting alles zusammen in einer großen Aussage:
PlotSignal(
CreateSignal(
CreateBaseSignal(signalType),
frequency,
amplitude),
thePictureBox);
Suchen Sie die Formel bitten, eine Linie zu zeichnen oder wie man tatsächlich die Zeile auf dem Formular setzen? – carny666
Was ist das Timer-Ding? Warum hörst du nicht auf das TrackBar.ValueChanged-Ereignis? –
@Nicolas: Timer ist für anderen Teil des Codes. Ich gebe die genauen Werte (Amplitude, Frequenz) an die Hardware-Ausgabe, so dass es Werte zur Ausgabe on-the-fly senden wird. Versucht, das reale Hardware-Ausgangssignal zu visualisieren. –