Ankündigung

Einklappen
Keine Ankündigung bisher.

Linearisierung mit Stützstellen - Funktioniert dieser Code?

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    Linearisierung mit Stützstellen - Funktioniert dieser Code?

    Das Problem ist alt und gut bekannt:
    Ich habe einen Eingangswert, zu dem ich einen Ausgangswert bestimmen möchte, wo bei der Zusammenhang sich schwer oder gar nicht als mathematische Formel darstellen läßt, wohl aber recht einfach als Kurve mit vielen Stützstellen und linearer interpolation dazwischen.
    Die Lösung ist auch alt und gut bekannt:
    Man hat eine Tabelle der Stützstellen (x/y) im Speicher die nach x sortiert ist. Das Programm sucht die beiden Punkte, deren x-Werte den gegebenen Eingangswert einschließen und berechnet den gesuchten y-Wert per linearer Interpolation.
    Wünschenswert wäre, wenn wie bei anderen Berechnungen auch, das Ergebnis noch im selben Zyklus vorliegen würde, in dem sich der Eingangswert geändert hat...

    Ich habe da an folgenden Code gedacht, kann ihn aber derzeit nicht testen. Würde der funktionieren?

    [highlight=epc]
    // Bei Systemstart das Array mit (x/y)-Wertepaaren belegen
    // dabei gilt x0 muss kleiner oder gleich dem kleinsten im Betrieb
    // für x zu erwartenden Wert sein, (sonst wird kein y berechnet)
    // der letzte x-Wert xn muss größer oder gleich dem groeßten im
    // Betrieb für x zu erwartenden Wert sein (sonst wird der Index
    // ueber das Array-Ende hinaus inkrementiert), und die x-Werte
    // muessen streng monoton steigen: x0<x1<x2<x3< ... <xn
    // Die y-Werte duerfen beliebig sein.
    if systemstart() then {
    i=1u16
    x=0.0f32
    y=0.0f32

    stringset(Array,x00,0u16)
    stringset(Array,y00,4u16)
    stringset(Array,x01,8u16)
    stringset(Array,y01,12u16)
    ...
    stringset(Array,x30,240u16)
    stringset(Array,y30,244u16)
    stringset(Array,x31,248u16)
    stringset(Array,y31,252u16)
    } endif

    // Wenn sich x aendert wird der Array-Index auf 0 gesetzt
    if change(x) then i=0u16 endif

    // Wenn der Index sich ändert neue Wertepaare aus dem Array holen.
    // Da es sein kann, das er beim letzten mal i mit 0 zurückgelassen wurde
    // und event() bei Variablen nicht funktioniert,
    // wird auch auf Aenderungen von x getriggert
    if change(i) or change(x) then {
    xu=stringcast(Array,x,i)
    yu=stringcast(Array,y,i+4)
    xo=stringcast(Array,x,i+8)
    yo=stringcast(Array,y,i+12)
    } endif

    // Wenn sich x oder die Vergleichswerte ändern, dann
    // wenn x innerhalb von xu und xo liegt, dann
    // y neu berechnen sonst weiter mit den naechsten Wertepaaren
    if change(x) or change(xu) or change(xo) then {
    if ( xu <= x) and (x <= xo) then y = (yo-yu)*(x-xu)/(xo-xu)+yo else i = i+8 endif
    } endif
    [/highlight]
    Tessi

    #2
    OK, die Frage ist wohl etwas zu allgemein und obiger Pseudo-Code ist so auch nicht compilierbar. Dieser hier ist es:
    [highlight=epc]
    [EibPC]
    i =0u16
    x =0.0f32
    y =0.0f32
    xu=0.0f32
    xo=0.0f32
    yu=0.0f32
    yo=0.0f32
    Array=$$
    // Bei Systemstart das Array mit (x/y)-Wertepaaren belegen
    // (immer im Wechsel x0,y0,x1,y1,x2,y2,...)
    // dabei gilt x0 muss kleiner oder gleich dem kleinsten im Betrieb
    // für x zu erwartenden Wert sein, (sonst wird kein y berechnet)
    // der letzte x-Wert xn muss größer oder gleich dem groeßten im
    // Betrieb für x zu erwartenden Wert sein (sonst wird der Index
    // ueber das Array-Ende hinaus inkrementiert, wird nicht abgefangen),
    // und die x-Werte muessen streng monoton steigen: x0<x1<x2<x3< ... <xn
    // Die y-Werte duerfen beliebig sein.
    // Im vorliegenden Code soll immer gelten 0 <= x <= 360
    // Ausserdem wird der Index auf das 6. Wertepaar gesetzt
    if systemstart() then {
    stringset(Array, -99.0f32, 0u16); stringset(Array, 100.0f32, 4u16);
    stringset(Array, 90.0f32, 8u16); stringset(Array, 100.0f32, 12u16);
    stringset(Array, 95.0f32, 16u16); stringset(Array, 10.0f32, 20u16);
    stringset(Array, 140.0f32, 24u16); stringset(Array, 10.0f32, 28u16);
    stringset(Array, 150.0f32, 32u16); stringset(Array, 30.0f32, 36u16);
    stringset(Array, 170.0f32, 40u16); stringset(Array, 40.0f32, 44u16);
    stringset(Array, 190.0f32, 48u16); stringset(Array, 40.0f32, 52u16);
    stringset(Array, 210.0f32, 56u16); stringset(Array, 30.0f32, 60u16);
    stringset(Array, 220.0f32, 64u16); stringset(Array, 10.0f32, 68u16);
    stringset(Array, 265.0f32, 72u16); stringset(Array, 10.0f32, 76u16);
    stringset(Array, 270.0f32, 80u16); stringset(Array, 100.0f32, 84u16);
    stringset(Array, 450.0f32, 88u16); stringset(Array, 100.0f32, 92u16);
    i = 40u16;
    } endif

    // Wenn der Index sich ändert neue Wertepaare aus dem Array holen.
    // Das sollte erstmals beim Systemstart erfolgen.
    if change(i) then {
    xu=stringcast(Array,x,i);
    yu=stringcast(Array,y,i+4u16);
    xo=stringcast(Array,x,i+8u16);
    yo=stringcast(Array,y,i+12u16);
    } endif

    // Wenn sich x oder die Vergleichswerte ändern, dann
    // wenn x innerhalb von xu und xo liegt, dann
    // y neu berechnen sonst je nachdem weiter mit den
    // naechsten oder den vorherigen Wertepaaren.
    if change(x) or change(xu) or change(xo) then {
    if ( xu <= x) and (x <= xo) then y = (yo-yu)*(x-xu)/(xo-xu)+yo endif;
    if (x < xu) then i = i-8u16 endif;
    if (xo < x) then i = i+8u16 endif;
    } endif

    [/highlight]

    Er ist ein wenig anders aufgebaut, das Array mit den Stützpunkten wird nicht mehr immer von Anfang an durchsucht sondern beginnend mit den beiden Punkten der letzten Berechnung. Bei sich nicht all zu sprunghaft ändernden Werten geht das schneller...

    Ab Zeile 2 werden die Variablen definiert, ab Zeile 22 wird bei Systemstart das Array mit Werten belegt und in Zeile 34 der Index neu gesetzt. Was ich nicht prüfen kann ist, in welcher Reihenfolge das erfolgt.
    Der Code funktioniert nicht, wenn der Code ab Zeile 2 nicht vor dem ab Zeile 22 ausgeführt wird, und auch Zeile 34 sollte erst nach der Initialisierung des Arrays ausgeführt werden, da sie den Code ab Zeile 40 triggert, und der braucht ein initialisiertes Array.
    Ebenfalls kann ich derzeit nicht prüfen, ob der Code in Zeile 40-43 und die Tests in Zeile 51-53 wechselweise so lange ausgeführt werden, bis ein neuer y-Wert berechnet werden konnte.

    Meine Frage ist einfach: Sind die als Voraussetzung notwendigen Annahmen von mir richtig, oder wird der Code nicht wie erwartet arbeiten, weil die interne Abarbeitung anders als von mir angenommen organisiert ist?
    Tessi

    Kommentar


      #3
      Hallo,

      probier es doch einfach mal aus, oder erwartest du das wir im Forum deinen Code mit entsprechenden Tabellen nachstellen und ausprobieren?
      Der schöne Niederrhein läßt Grüssen

      Andreas


      Alter Hof mit neuer Technik

      Kommentar


        #4
        Ich habe da mebr auf eine theoretische Analyse gehofft. Da ich kein Timing vorgebe, bin ich unsicher, ob die von mir angenommene Reihenfolge bei der Abarbeitung der einzelnen Ausdrücke tatsächlich so ist, wie sie gebraucht wird und vor allem ob sie dann auch immer so ist oder ob das dann je nach Situation auch verschieden sein kann.
        Tessi

        Kommentar


          #5
          Hauptproblem:
          Wegen des ja allen bekannten change() delays und der fehlenden Möglichkeit Schleifen zu implementieren, hängt die Ausführung stark von der Größe des Arrays ab -> für eine möglichst kurze Laufzeit solltest du (natürlich nur falls überhaupt möglich -> Speicherplatz) die X-Punkte durch das Einfügen weiterer (unnötiger) Stützpunkte linearisieren. Dann kannst du eine Zugriffsfunktion nutzen.

          -> In deinem Beispiel die beiden Grenzwerte im Code abfangen und die Schrittweite auf 5 setzen (bzw. bei 95 einen Fehler hinnehmen) -> Zugriff ist Offset + x/5.

          Ansonsten kannst du die Events besser beachten, d.h. Abhängige Ausführung in Verschachtelungen unterbringen -> i ändert sich nur bei Systemstart oder wenn sich x ändert (-> code für xn doppelt). Da ja das change() dann im nächsten Zyklus wirkt, sollte das doppelt so schnell sein.

          Das change() ist allerdings was für Schachspieler
          BR
          Marc

          Kommentar


            #6
            Moin Tessi,

            Wünschenswert wäre, wenn wie bei anderen Berechnungen auch, das Ergebnis noch im selben Zyklus vorliegen würde, in dem sich der Eingangswert geändert hat...
            Was willst Du denn genau machen und warum ist das so zeitkritisch?

            Ich hatte da mal was entwickelt, weiß aber nicht, ob es zu Deinem Problem passt. Aber schau mal bei den Codeschnipseln danach.

            Gruß,
            Bernd

            Kommentar


              #7
              Erst einmal Danke für die Antworten.

              Zeitkritisch ist die derzeit geplante Anwendung eher nicht. Der 1-Zykluswunsch entspringt dem Wunsch, das ganze als Makro zu implementieren und dann wie z.B. sin(x) verwenden zu können, da steht das Egebniss ja auch im selben Zyklus zur Verfügung. Das ist später dann rein gedanklich einfacher von der Anwendung, wenn man keinen Zeitversatz berücksichtigen muss.

              Das change() immer erst einen Zyklus nach der Änderung triggert, hatte ich schlicht vergessen. Eine 1-Zyklus Ausführung ist dann nicht möglich. Dafür kann ich dann aber den Ablauf besser vorhersagen, da pro Schritt zwei Zyklen gebraucht weden, und so halbwegs sicher ist, das die notwendige Reihenfolge eingehalten wird.

              Ich möchte die Funktion selbst möglichst flexibel halten, daher keine feste Anzahl an Stützstellen und auch keinen Zwang zur Linearisierung, damit fällt eine Zugriffsfunktion leider aus, gleiches gilt für alle Optimierungen, die bestimmte Voraussetzungen fordern. Denkbar wäre dagegen eine Suchfunktion, die statt linear von vorne nach hinten zu vergleichen Intervalle einschachtelt. Ob das aber bei vielleicht 5 bis 20 Stützpunkten sich schon lohnt...

              Die Codeschnipsel muss ich mir noch mal genauer ansehen, so auf die Schnelle habe ich das Vorgehen da noch nicht verstanden.

              Eine Sache ist noch, das ich die Liste der Stützpunkte gerne mit variabler Länge per Webinterface anpassen können möchte. Das erfordert voraussichtlich eine indizierte Ansprechbarkeit der Einträge zur Laufzeit. "Namenstricksereien" in Makros fallen da wohl aus, es bleibt wohl nur ein per String simuliertes Array...

              Ich würde den Code ja schon gerne direkt testen, nur fehlt mir da ein Single-Step-Modus mit der Möglichkeit die Veränderungen alle genau und in Ruhe zu sehen. Deshalb bleiben erst einmal nur theoretische Analysen, aber da bin ich halt nicht immer sicher, wie das System tatsächlich im Einzelfall reagiert. Deshalb bitte ich hier ja um Kommentare
              Tessi

              Kommentar


                #8
                Naja, funktionieren tut der Code den ich gepostet habe schon und das auch innerhalb eines Zyklus. Wenn Du unbedingt einen Index nutzen willst: Da ist auch einer enthalten bei jedem Datenpunkt. Dann mußt Du nur eine andere Logik entwickeln um unabhängig vom X-Eingangswert das Ergebnis zu ermitteln. Nur hat das dann nicht mehr viel mit Lookuptabelle zu tun.

                Gruß,
                Bernd

                Kommentar


                  #9
                  Irgendwie habe ich momentan Probleme den Code zu compilieren...
                  Ich bin auch nicht sicher, ob ich ihr ganz verstanden habe.
                  Es wird für jeden Punkt der Tabelle Code eingebunden, der die für die Approximation verwendeten Punkte ermittelt.
                  Da könnte bei großen "Tabellen" schon einiges an gleichzeitig getriggertem Code zusammenkommen...
                  Mich beschäftigt da die Frage, wie stelle ich sicher, das die Approximation erst nach der Bestimmung von x0/y0 und x1/y1 erfolgt? Beides wird durch das selbe Ereignis getriggert und ich habe im Handbuch keine Hinweise auf die Abarbeitungsreihenfolge finden können...
                  Das Reihenfolge-Problem würde auch für die Bestimmung von x0/y0 und x1/y1 selbst gelten, aber so wie es aussieht, ist es egal, in welcher Reihenfolge die verglichen werden, es bleibt für x0 der größte Wert kleiner oder gleich xin und für x1 der kleinste Wert größer oder gleich xin über. Da ist es gar nicht zwingend notwendig, das AddStaticPoint() mit aufsteigenden Werten für x aufgerufen wird, die "Tabelle" muss gar nicht sortiert sein...

                  Kann jemand mit Bestimmtheit sagen, in welcher Reihenfolge Statements abgearbeitet werden, die durch dasselbe Ereignis getriggert werden, aber sonst nicht voneinander abhängig sind? Der obige Code funktioniert nur, wenn in den Fällen der Code tatsächlich in der Reihenfolge ausgeführt wird, in der er im Programm auftritt. Aber ist das auch so?

                  Nun ja, erst einmal muss ich das compiliert bekommen, dann kann ich mal anfangen zu testen.
                  Tessi

                  Kommentar


                    #10
                    Zitat von Tessi Beitrag anzeigen
                    Irgendwie habe ich momentan Probleme den Code zu compilieren...
                    Also den Code packst Du Dir in eine Makro-Datei. Dann im EibPC rufst Du auf
                    SetupStaticLookupTable( NAME, 0.0f32, 360.0f32 )
                    //
                    // Dann pro Datenpunkt den man haben möchte ein XY-Paar anlegen.
                    // Dabei muß darauf Rücksicht genommen werden, das die Werte aufsteigen
                    // und keine negative Steigung entsteht.
                    AddStaticPoint(NAME, 0.0f32, 100.0f32 )
                    AddStaticPoint(NAME, 90.0f32, 100.0f32 )
                    AddStaticPoint(NAME, 95.0f32, 10.0f32 )
                    und so weiter ...
                    AddStaticPoint(NAME, 360.0f32, 100.0f32 )
                    // Der tatsächliche Vergleich erfolgt in diesem Makro, dort
                    // wir auch der Sonderfall x0 == x1 berücksichtigt.
                    DoStaticLookup(NAME)


                    Es wird für jeden Punkt der Tabelle Code eingebunden, der die für die Approximation verwendeten Punkte ermittelt.
                    Da könnte bei großen "Tabellen" schon einiges an gleichzeitig getriggertem Code zusammenkommen...
                    Ja absolut richtig. Aber solange es im EibPC nichts wie Arrays gibt und keine intrinsische Lookup-Funktion schien mir das die schnellste Variante.


                    Mich beschäftigt da die Frage, wie stelle ich sicher, das die Approximation erst nach der Bestimmung von x0/y0 und x1/y1 erfolgt?
                    Du könntest einen Konstrukt if after(change(x0),1u64) or after(change(x1),1u64) then ... endif nehmen. Ansonsten kannst Du eine State-Machine anwenden, also eine Zählvariable, die immer einen bestimmten Status definiert.

                    Es wäre natürlich schön, wenn ein offizielles Statement von enertex dazu käme ...

                    Gruß,
                    Bernd

                    Kommentar


                      #11
                      Zitat von bmx Beitrag anzeigen
                      Es wäre natürlich schön, wenn ein offizielles Statement von enertex dazu käme ...
                      Ja, aber ich ahne da nichts gutes, deren Makros zum Thema Schleifen arbeiten ja auch mit Timern!!!
                      Leider fehlt da ein "mach im nächsten Zyklus weiter"-Timer, aber ggf. kann man da was mit change() machen...

                      Den Code hatte ich in eine Datei kopiert und dann den auskommentierten Header noch in die Programmdatei kopiert und vor den Makroaufrufen die Kommentare entfernt. Ergebniss:
                      DoStaticLookup(NAME) hat angeblich kein return und keine Anweisung vor dem end...
                      Keine Ahnung, warum der Compiler das nicht sieht, denn es ist definitiv da.
                      Kommentiere ich diesen Makroaufruf aus, kommen andere Fehler...
                      Irgendwie ist da bei mir der Wurm drin. Aber ich versuche es weiter sobald ich wieder Zeit habe.

                      Echte Arrays hat ner EibPC nun leider nicht, aber mit stringset() und stringcast() kann man wenigstens rudimentär was nachbilden.

                      Was wirklich fehlt sind Schleifen. Wobei die Dank der der Verzögerung von change() um je einen Zyklus nachgebildet werden können. Im Sinne einer begrenzten Last pro Zyklus mag es ja OK sein, das eine Schleife dann pro Zyklus genau einen Durchlauf macht.
                      Tessi

                      Kommentar


                        #12
                        Vorsicht: stringset ist wiederum verzögert lt. Handbuch...

                        Ach ja, ich habe das EibStudio 2.113 genutzt und mit FW 2.121 getestet.

                        Gruß,
                        Bernd

                        Kommentar


                          #13
                          Zitat von Tessi Beitrag anzeigen
                          Was wirklich fehlt sind Schleifen. Wobei die Dank der der Verzögerung von change() um je einen Zyklus nachgebildet werden können. Im Sinne einer begrenzten Last pro Zyklus mag es ja OK sein, das eine Schleife dann pro Zyklus genau einen Durchlauf macht.
                          Schleifen weg zu lassen begrenzt die Last pro Zyklus nicht, denn der Anwender findet andere Lösungen! Eine Schleife mit einem Schritt pro Zyklus ist Augenwischerei. Was da alles passieren kann, während die Schleife ausgeführt wird?

                          Da soll halt einen Hinweis in die Anleitung, genauso wie Michael es mit den verschachtelten if-then macht, und nicht der Anwender bevormundet werden.

                          Das zeigen die Makros von Bernd ja sehr schön. Da wird pro Tabelleneintrag einfach entsprechender Code erzeugt. Das verringert weder die Last pro Zyklus noch den Speicherverbrauch, aber dafür erfüllt es den Zweck!?
                          BR
                          Marc

                          Kommentar


                            #14
                            Ja, mit etwas Kreativität kann ich das System auch so überlasten, aber der Code pro Zyklus bleibt endlich. Mit echten Schleifen kann ich die Zahl der Operationen pro Zyklus extrem groß werden lassen. Das war Enertex wohl zu heikel.
                            Trotzdem hätte ich gerne welche gehabt...

                            Was stringset() angeht, wie groß ist da denn das Delay? Und stringcast() hat keines?
                            Tessi

                            Kommentar


                              #15
                              Zitat von Tessi Beitrag anzeigen
                              Ja, mit etwas Kreativität kann ich das System auch so überlasten, aber der Code pro Zyklus bleibt endlich. Mit echten Schleifen kann ich die Zahl der Operationen pro Zyklus extrem groß werden lassen. Das war Enertex wohl zu heikel.
                              Das hat auch nichts mit "extrem groß" oder "Kreativität" zu tun, denn es geht um "Fehler machen" und das kann ich so oder so. Denn ohne Fehler keine Angst vor Schleifen, oder?

                              Das eigentliche Problem ist also, dass der eibPC entweder nicht die nötige Fehlerbehandlung besitzt (bezweifel ich allerdings), sie nicht ausreichend Dokumentiert ist (eher wahrscheinlich, siehe EVENT Speicher), der Bedarf nicht gesehen wird (sehr wahrscheinlich) oder dem Anwender pauschal unterstellt wird, dass er erst einmal nicht richtig programmieren bzw. die Fehler dann nicht finden kann. Dann sollte man endlich einmal die entsprechenden Möglichkeiten schaffen und zwar unabhängig davon, ob es Schleifen gibt oder nicht!

                              Trotzdem hätte ich gerne welche gehabt...
                              dito.
                              BR
                              Marc

                              Kommentar

                              Lädt...
                              X