Welle bei kleiner Wellenlänge fehlerhaft gezeichnet

  • C#

SSL ist deaktiviert! Aktivieren Sie SSL für diese Sitzung, um eine sichere Verbindung herzustellen.

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von Pascalony.

    Welle bei kleiner Wellenlänge fehlerhaft gezeichnet

    Hey,

    ich versuche momentan Transversalwellen grafisch darzustellen. Das scheint bei größeren Wellenlängen kein Problem zu sein, jedoch verhält sich die Welle bei Wellenlängen < ca 4 extrem merkwürdig. Die Amplitude wird nicht wirklich eingehalten, generell ist das Wellenbild verzerrt. Das Problem ist schwer zu beschreiben, aber extrem einfach zu sehen, deshalb werd ich einfach mal das Projekt anhängen, dann kann es jeder selbst sehen. Einfach debuggen und beim der Trackbar für die Wellenlänge den Regler verschieben. Zusätzlich hänge ich noch die Klassenbibliothek an, mit der ich eigentlich programmiere. Die Forms-Anwendung ist nur ein Testprogramm.

    An die, die sich die Projektmappe nicht runterladen können/wollen stelle ich die wichtigsten Infos hier zusammen:

    C#-Quellcode

    1. //Welle updaten (Position der Oszillatoren:
    2. public void Update()
    3. {
    4. if(Direction == WaveDirection.LeftToRight)
    5. {
    6. for (var i = 0; i < Oscillators.Count; i++)
    7. Oscillators[i] = new PointF(Oscillators[i].X, (float)(Amplitude * Math.Sin((double)(2 * Math.PI * ((Watch.ElapsedMilliseconds * 0.001f / Period) - (Oscillators[i].X / WaveLength))))));
    8. }
    9. else
    10. {
    11. for (var i = 0; i < Oscillators.Count; i++)
    12. Oscillators[i] = new PointF(Oscillators[i].X, (float)(Amplitude * Math.Sin((double)(2 * Math.PI * ((Watch.ElapsedMilliseconds * 0.001f / Period) + (Oscillators[i].X / WaveLength))))));
    13. }
    14. }


    C#-Quellcode

    1. //Welle zeichnen (Surface = Bitmap auf die gezeichnet wird, Offsets sind die Verschiebungen in X/Y-Richtung)
    2. public void DrawOscillators(SizeF oscillatorsize, Brush brush)
    3. {
    4. float distancefactor = (Surface.Width - OffsetX) / Oscillators.Count;
    5. for (var i = 0; i < Oscillators.Count; i++)
    6. _device.FillEllipse(brush, new RectangleF(new PointF(OffsetX + Oscillators[i].X * distancefactor , OffsetY + Oscillators[i].Y), oscillatorsize));
    7. public void DrawCurve(Pen pen)
    8. {
    9. float distancefactor = (Surface.Width - OffsetX) / Oscillators.Count;
    10. PointF[] temp = Oscillators.ToArray();
    11. for (var i = 0; i < Oscillators.Count; i++)
    12. temp[i] = new PointF(OffsetX + temp[i].X * distancefactor /** 12.5f*/, OffsetY + temp[i].Y * distancefactor);
    13. _device.DrawCurve(pen, temp);
    14. }
    15. }


    Zusätzlich hänge ich noch ein Bild an, wie es aussehen sollte, und wie es teilweise aussieht. Bild 1 ist das "normale" Bild.

    Edit: Rote und blaue Welle sind identisch, die gelb/grüne ist die Interferenz der beiden Wellen.
    Bilder
    • 1.png

      24,68 kB, 1.399×748, 49 mal angesehen
    • 2.png

      41,83 kB, 1.403×745, 37 mal angesehen
    • 3.png

      35,75 kB, 1.398×740, 41 mal angesehen
    Dateien

    @Pascalony Das zweite Bild sieht mir einfach untersampled aus, setze die Stützstellen dichter, 20 pro Periode, und alles sollte fein aussehen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    20 pro Periode


    Meinst du jetzt bei der Berechnung oder beim Zeichnen? Mein Problem ist, dass ich bei der Berechnung der Oszillatoren immer nur ganzzahlige Werte habe und es deshalb ungenau ist.

    So sind die Oszillatoren initialisiert, d.h die X-Koordinate ist immer ganzzahlig:

    C#-Quellcode

    1. for(var i = 0; i < count; i++)
    2. Oscillators.Add(new PointF(i, offsetY));


    C#-Quellcode

    1. //Da ich hier mit der X-Koordinate rechne, ist es extrem ungenau (bei einer normalen Welle ist der Abstand der Oszillatoren deutlich kleiner.)
    2. Oscillators[i] = new PointF(Oscillators[i].X, (float)(Amplitude * Math.Sin((double)(2 * Math.PI * ((Watch.ElapsedMilliseconds * 0.001f / Period) + (Oscillators[i].X / WaveLength))))));


    Also ist das Bild jetzt wegen meiner Rechnung untersampled oder wegen der Art wie ich es zeichne, ich steh voll aufm Schlauch. Ich gucke mir den Code seit gestern immer wieder an...

    @Pascalony beim Zeichnen.
    Dem Rechnen ist das egal, die Ansicht ist einfach nicht gut.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!
    @RodFromGermany

    Wie würdest du das denn trennen? Ich habe eine Liste von Oszillatoren. Die haben die berechneten Koordinaten. Wenn ich jetzt mehr Oszillatoren der Liste hinzufüge, verändert sich doch auch meine Rechnung oder? Ich wüsste nämlich nicht was ich allein an der Zeichen-Methode ändern soll.

    @Pascalony Ich weiß jetzt nicht genau, wie Du das rechnest.
    Aber irgendwo muss doch so was wie eine Sample-Rate auftauchen, die musst Du anpassen, am besten in der Einhait Samples pro Wellenlänge.
    Es ist natürlich klar, dass Du nur gerechnete Punkte darstellen kannst.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!
    @RodFromGermany

    Die Oszillatorenanzahl kann im Konstruktor angegeben werden. Also mal ein Beispiel:

    Die Oszillatoren werden in der Schleife initialisiert, d.h der erste Oszillator hat die Position (0, OffSetY), der zweite die Position (1, OffsetY) usw.
    Dann wird bei jedem Aufruf von Update die Position neu berechnen (nur die Y-Position wird berechnet, X bleibt gleich). Berechnet wird nach der Formel:

    s(x, t) = Amplitude * sin(2 * PI * ( (t / T) - (x / lamda))

    t = momentaner Zeitpunkt
    T = Periode
    x = Position des Oszillators
    lambda = Wellenlänge

    Folgendes passiert nun beim Zeichnen:

    Es wird ein Faktor (distancefactor) ausgerechnet, der sagt wie groß der Abstand zwischen den gezeichneten Oszillatoren ist (sonst würde man nur eine winzige Welle erkennen). Der berechnet sich durch die Breite der Form durch die Anzahl der Oszillatoren. Sagen wir Formbreite = 800 also 800 / 100 = 8.
    Dann zeichne ich den Oszillator also an der Stelle Oszillator.X (ändert sich ja nie, immer ganzzahliger Wert) * distancefactor + OffSetX (falls ich die Welle halt verschieben will).
    Die Y-Position eines Oszillators ist der, den ich vorher berechnet habe + das OffsetY (falls ich die Welle nach oben oder unten verschiebe).

    Mit der Art wie ich rechne und zeichne weiß ich nicht, was ich wo ändern muss, damit das Bild nicht undersampled ist.

    Hey! Der gute alte Alias-Effekt!
    Wie nennt man diesen Effekt
    Ist auch schon lange her!
    Das passiert, wenn die Wellenlänge gegen 1 Pixel und kleiner geht.
    Probiere zum Testen mal, was rauskommt, wenn Du das komplette Argument an Math.Sin nochmal durch 10 dividierst. Dann wird die Wellenlänge auf das 10-fache vergrößert und sollte damit wieder gut darstellbar sein.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @RodFromGermany

    Ein Oszillator ist in meinem Programm ein PointF. Die Elongation wird bei mir durch die Y-Position dargestellt.

    Edit: @Niko Ortner
    Ich verstehe den Effekt nicht wirklich, kannst du den genauer erklären (Der Effekt betrifft doch die Frequenz?)? Und zu deinem Lösungsvorschlag: jetzt wird genau gezeichnet bis zu einer Wellenlänge von 0.3 ungefähr. Also einfach durch eine noch höhere Zahl dividieren?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Pascalony“ ()

    Pascalony schrieb:

    Ich verstehe den Effekt nicht wirklich
    Wenn Du bei einer Schwingung pro Wellenlänge genau einen Punkt darstellst, bekommst Du eine Gerade.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Wenn Du bei einer Schwingung pro Wellenlänge genau einen Punkt darstellst, bekommst Du eine Gerade.


    Achso, jetzt verstehe ich. Dann macht es auch Sinn warum ich mehr Oszillatoren brauche. Mit der Kombination eurer Lösungen wird auch bei niedrigen Wellenlängen ein gutes Bild draus, danke! Für alle die es interessiert hänge ich mal ein Vergleichsbild an:

    Wellenlänge: ca 1
    Oszillatoren: 500

    Wellenlänge: ca 1
    Oszillatoren: 10
    Bilder
    • 10.png

      13,35 kB, 1.397×708, 32 mal angesehen
    • 500.png

      32,83 kB, 1.400×712, 34 mal angesehen

    Hi
    dafür habe ich schon mal ein paar einfache Algorithmen entworfen. Einer davon ist, dass du eine nach X sortierte Liste von Punkten hältst und solange Punkte zwischen zwei benachbarten Punkten einfügst, bis deren Abstand unterhalb einem gewissen Intervall liegt. Das führt zwar bei Hoch- und Tiefpunkten zu Problemen, aber bei klein genugem Intervall bereits zu ganz guten Lösungen. Außerdem lässt es sich parallelisieren.

    Für die n-fache Parallelität dividiere das anfängliche Intervall (xStart, xEnd) in sinnvolle Abschnitte (für "sinnvoll" gibt es auch elegante Algorithmen, aber unterteile sie am besten einfach in m + n Abschnitte und lasse dann jeweils den Thread, der zuerst fertig ist den nächsten Abschnitt berechnen. Abschließend füge alle Ergebnisse zusammen und plotte sie).

    Eine recht praktische Datenstruktur ist an dieser Stelle übrigens ein Stack (außer du rechnest es rekursiv, das geht auch).
    Achte darauf, dass du ein sinnvolles Abbruchkriterium einführst, 1/x führt bei dem Verfahren sonst zu einem nicht haltenden Algorithmus.

    DrawCurve ist für ein derartiges Verfahren übrigens definitiv nicht sinnvoll. Was ist, wenn du z.B. Abs oder Sign plottest?

    Viele Grüße
    ~blaze~
    @~blaze~

    Das klingt eigentlich nach einer richtig guten Idee, aber irgendwie kann ich das nicht so einfach umsetzen, wenn jemand einen Algorithmus so beschreibt. Ich muss das jetzt erst mal in meinem Kopf zusammenbasteln. Das eigentlich verwirrende ist das Parallelisieren. Threading ist das einzige Basicthema, vor dem ich mich immer gedrückt habe.

    Um das mit etwas zu vergleichen, das ich schon kenne: Kann ich mir das wie Mergesort vorstellen? Ein Thread kann die linke Teilliste sortieren, ein andere die rechte? Und das dann immer weiter rekursiv, bis beim rekursiven Aufstieg die Ergebnisse der Threads zusammengeführt werden?

    Immerhin verstehe ich glaube ich das Prinzip, das du meinst. Also quasi so viele Oszillatoren zwischen meine Oszillatoren einfügen, bis der Abstand zwischen den Oszillatoren so klein ist, dass die Ungenauigkeit sich der null annähert (unmöglich, aber ich mein halt vom Prinzip her).

    Schreibe den Algorithmus erst mal für einen Thread, es lässt sich ganz einfach auf mehrere übertragen. Sorge dafür, dass der Output in einer Liste steht.

    Nicht Oszillatoren einfügen, sondern Punkte. Die Null ist in deinem Fall ein Epsilonwert (z.B. 1 oder 0.5 oder was dir sinnvoll erscheint).
    Mit Mergesort würde ich es nicht vergleichen, aber ggf. ist dir das Prinzip schon halbwegs klar.
    Nimm einfach zwei Punkte (xStart, f(xStart)) und (xEnd, f(xEnd)) und gehe ausgehend von denen "rekursiv nach unten" durch, bis der Abstand kleiner ist, als das Epsilon (d.h. ((x1 - x2)² + (y1 - y2)²) < epsilon², wobei ich vielleicht beidseitig durch epsilon dividieren würde. Beachte, dass z² = z * z effizienter ist, als Math.Pow).
    Ich würde allerdings ein iteratives Verfahren bevorzugen, d.h. der einfachste Ansatz (ist aber nicht ganz günstig) wäre, die sortierte Liste einfach solange zu durchlaufen, bis eine maximale Durchlaufzahl erreicht wird oder keine weiteren Punkte mehr hinzugefügt wurden und dabei in jedem Schritt zwei aufeinanderfolgende Punkte zu vergleichen. Am besten gehst du dabei von hinten nach vorne durch die Liste.

    Ich würde auch nicht unbedingt auf SortedList zurückgreifen, das wird sonst noch ineffizienter, als das, was ich bisher vorgeschlagen habe. LinkedList wäre zwar eigentlich ein gutes Mittel, aber dadurch ist der Speicher recht fragmentiert und Allokationen sind recht teuer. Normal würde ich eine eigene Datenstruktur implementieren, aber das ist vermutlich Overkill für solch "kleine" Plots.

    Kleiner Tipp: Wenn du an Hochpunkten und Tiefpunkten keine Ungenauigkeit möchtest, erhöhe die Messwertdichte dort (z.B. anhand der Steigung kannst du das abschätzen, allerdings setzt sich das dort fort: Jedes Hinzufügen an Genauigkeit kann weitere Tief-/Hochpunkte offenbaren).

    Viele Grüße
    ~blaze~

    ~blaze~ schrieb:

    Nicht Oszillatoren einfügen, sondern Punkte.


    Das hat mich jetzt ganz verwirrt. Ich soll doch die Punkte in meine Liste mit Punkten einfügen. Die Y-Koordinate dieser Punkte werden doch auch durch die Wellengleichung berechnet. Sind es dann nicht auch Oszillatoren? Ich soll diese Punkte doch dann auch zeichnen? Hab mal ein Bild angehängt wie ich das verstanden habe.

    Rot = Oszillatoren
    Grün = eingefügte Punkte (auch Oszillatoren)

    Mit dem Mergesortvergleich hab ich nur gemeint, dass man bei Mergesort Aufgaben in Threads unterteilen kann, weil sie voneinander unabhängig sind (so wie das Einfügen der Punkte). Das ist doch dann auch Parallelisierung.
    Bilder
    • 500.png

      13,69 kB, 1.399×220, 29 mal angesehen

    Also ich verstehe unter einem Oszillator die Generatoren, die deine Schwingungen erzeugen. Rot ist insofern für mich ein einziger Oszillator, dessen geplottete Punkte durch die grünen Punkte weiter verfeinert werden.
    Wenn ich es so verstanden habe, wie du meinst, verhält es sich wie Mergesort, in diesem Aspekt. Man teilt das Intervall halt in Einzelsegmente auf, da die alle unabhängig voneinander sind.

    Viele Grüße
    ~blaze~
    Eine Welle besteht aus Oszillatoren, die nach oben und unten schwingen. Das heißt, dass bei mir jeder rote Punkte in Oszillator ist. Der Oszillator hat eine X-Koordinate, und eine Y-Koordinate, die die Auslenkung darstellt. Die Oszillatoren sind in einer List<PointF> gespeichert. Wenn ich jetzt Punkte zwischen den Oszillatoren einfüge, sind diese Punkte ja auch Oszillatoren. Aber dann kann ich doch einfach die Oszillatorenanzahl im Konstruktor erhöhen, dadurch wird der Abstand doch auch immer kleiner oder?

    Verwechselst Du gerade Oszillatoren (Gerät zur Erzeugung von Schwingungen) mit Oszillationen (Schwingungen)?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von VaporiZed, mal wieder aus Grammatikgründen.

    ― Eine häufig von mir verwendete Abkürzung: CEs = control elements (Labels, Buttons, DGVs, ...)
    ― If Not GrammarIsOk() Then AssumeThatCodeIsOk = False
    ― »Oh, großes Spaghetticodemonster. Bitte schicke mir Durchblick! Oder zumindest eine Gabel. Oder – wenn es kein Besteck mehr gibt – zumindest Glasnudeln.«