Seite 1 von 2

Ungenauer Timer?

Verfasst: Donnerstag 27. Juli 2006, 04:58
von JulesV
Hallo
Ich brauche dringend eine zuverlässige und einfache Möglichkeit der Zeitmessung.
Bisher hatte ich den wxtimer benutzt - der unter OS X auch prima zu funktionieren scheint, aber unter windows nicht. Ich paste hier mal ein Codebeispipel dass es verdeutlicht - oder bei dem ihr mir sagen könnt was ich ändern könnte um es zuverlässig zu machen.
Es geht darum dass ich über einige Minuten hinweg eine genaue Zeit brauche. Ich rechne hier mit 60 Sekunden * 1000 (milli) / 40 (timerintervall).
Setze ich mich mit einer Stopuhr daneben kommen auf unterschieden windows-kisten immer die selben falschen zeiten raus. Bei 10 Minuten wird der Unterschied natürlich noch drastischer...

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import wx
import time


class MainWindow(wx.Frame):

    def on_timer(self, event):
        self.counter+=1
        if self.counter==60000/self.ms:     # 1 minute
            self.timer.Stop
            print "done"

    def __init__(self):
        wx.Frame.__init__(
            self,
            None,
            wx.ID_ANY,
            "60 seconds",
            size=(200,100),
            style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
        )

        self.counter=0
        # Timer erstellen, Event an Funktion binden und Timer starten
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
        self.ms=50
        self.timer.Start(self.ms)
        print "start"
        self.Show(True)


app = wx.PySimpleApp()
frame = MainWindow()
app.MainLoop()
()

Ich hoffe mir kann jemand helfen... denn ich brauche den timer um eine Animation/Simulation zu steuern die Zeitkritisch ist... (was unter OS X auch zu funktionieren scheint - aber eben nicht unter windows).

Liebe Grüße
Jules

Verfasst: Donnerstag 27. Juli 2006, 06:34
von Damaskus
Hi, wieviel abweichung hast du denn gemessen?
Also ich habs mal auf meiner Win Kiste getestet und bin nur auf einige Millisekunden gekommen.

Gruß
Damaskus

Verfasst: Donnerstag 27. Juli 2006, 07:06
von Nirven
Die Abweichung beträgt ca ein viertel bei mir, wenn du bei on_timer print time.time() einfügst siehst du, dass die Funktion anscheinend alle 62,5 ms aufgerufen wird (bei mir zumindestens). Dementsprechend dauert ein 6s-Intervall (Bedingung auf 6000/self.ms) bei mir auch 7,5 Sekunden.
Ist schon eine recht deutliche Abweichung. Wenn die Abweichung auf verschiedenen Rechnern konstant ist könnte man ja zur Not damit rechnen.

Ich habe eine Abfrage auf das BS eingefügt, und wenn das Windows ist einfach die Abbruchbedingung auf 48000/self.ms ändern, dann kommt bei mir das richtige Ergebniss raus.

Achja, bei dem timer.Stop() fehlten die (), der lief weiter bis das Frame geschlossen wird.
Ich hab den Output auf time.time() geändert um das Ergebniss kontrollieren zu können.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import wx
import time
from sys import platform

class MainWindow(wx.Frame):

    def on_timer(self, event):
        self.counter+=1
        if self.counter==self.terminate/self.ms:     # 1 minute
            self.timer.Stop()
            print time.time()

    def __init__(self):
        wx.Frame.__init__(
            self,
            None,
            wx.ID_ANY,
            "60 seconds",
            size=(200,100),
            style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
        )
        
        if platform == 'win32':
            self.terminate = 48000
        else:
            self.terminate = 60000
        
        self.counter=0
        # Timer erstellen, Event an Funktion binden und Timer starten
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
        self.ms=50
        self.timer.Start(self.ms)
        print time.time()
        self.Show(True)


app = wx.PySimpleApp()
frame = MainWindow()
app.MainLoop()

Verfasst: Donnerstag 27. Juli 2006, 09:59
von Nirven
Ich hab noch ein bischen rumprobiert:

Ab einem Wert von 241 für self.ms läuft es bei mir akurat, 60 Sekunden sind auch 60,00 Sekunden (nicht mehr 75,00 wie es mit self.ms = 50 der Fall war). Scheint das Windows mit solch kleinen Intervallen Probleme hat (?).

Verfasst: Donnerstag 27. Juli 2006, 12:29
von JulesV
Habt ihr eine Idee wie ich dies Problem umgehen könnte:
Ich arbeite mit variablen Zeiten. Es geht um eine Rennstrecke, man gibt die länge ein und die Geschwindigkeit. Also z.B. 10 km bei 600 km/h wären wieder 60 Sekunden.
Die Simulation muss nun also genau 60 Sekunden laufen und dabei eine Animation ausführen (über den Timer alle 40 ms).
Ich rechne also
10 / 600 * (3600000 / 40) = 1500
Also lasse ich den Timer alle 40 Millisekunden 1500 Mal aufrufen und dabei die Animation ein Bild weitersetzen.
Das gesamte Programm ist fertig und muss in einer Woche abgegeben werden - mit Ausnahme dieses Timer Problems... *panik*

Liebe Grüße
Jules

Verfasst: Donnerstag 27. Juli 2006, 13:05
von Nirven
Wie gesagt, bei mir ist der Fehler konstant. Teste das Script mal mit meinen Änderungen. Beim starten wird die aktuelle Systemzeit zum Stdout geschickt und beim beenden ebenfalls. Dann einfach die Differenz bilden (oder das gleich ins Script einarbeiten).
Wenn da immer 6 Sekunden zwischenliegen, dann ist der Fehler konstant und mein Korrekturfaktor korrekt.

Du kannst einfach die Abfrage übernehmen ("if platform == 'win32':") und deine Konstante entsprechend anpassen (auf 4/5 reduzieren, also 2880000 statt 3600000).
Dann sollte das richtige Ergebniss rauskommen.

Oder du nimmst einfach 250ms als Intervall und sagst es ist eine schnelle Slideshow ;)

Verfasst: Freitag 28. Juli 2006, 03:55
von JulesV
Nirven hat geschrieben:Wie gesagt, bei mir ist der Fehler konstant. Teste das Script mal mit meinen Änderungen.
...
Du kannst einfach die Abfrage übernehmen ("if platform == 'win32':") und deine Konstante entsprechend anpassen (auf 4/5 reduzieren, also 2880000 statt 3600000).
Dann sollte das richtige Ergebniss rauskommen.
Leider funktioniert das nicht. In dem Testscript von oben komme ich bei mir auch auf 60 Sekunden mit deinem Korrekturwert, aber wenn ich diese paar Zeilen einfüge in meine Simulation stimmt die Zeit trotzdem nicht.

Ich habe mein Programm hier mal online gestellt damit es nachvollziehbar wird. Ausgeführt werden muss die simulation.py
http://www.schreibholz.de/jules/dateien/splinesim.zip

Einfach ein paar Punkte mit der Maus setzen, "Spline schließen" klicken, Streckenlänge und Geschwindigkeit setzen (z.B. 10 und 600 für eine Minute) und "Starten" klicken.

Ich brauche wirklich hilfe - wie gesagt in einer Woche ist Abgabe und das Programm entscheidet maßgeblich darüber ob die letzten 4 Jahre meines Lebens komplett für die Katz waren...

Verfasst: Freitag 28. Juli 2006, 07:57
von Nirven
Jo, der Faktor passt nur bei einem Intervall von 50ms. Bei 40ms kommt da was weniger schönes raus, statt 2880000.0 ist der Faktor dann 3072546.23 (und einige Stellen mehr, aber das sollte nicht den großen Unterschied machen). Mit dem neuen Faktor passt es bei mir wieder.

Da ist noch ein Problem in dem Programm, wenn es einmal rumgelaufen ist kommt sowas:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\eclPy\dummy\src\splinesim\simulation.py", line 126, in OnTimer
    self.track.getPointThread(self.punkte)
  File "C:\eclPy\dummy\src\splinesim\track.py", line 36, in getPointThread
    self.actualPoint=self._wayTable[self.time-1]
IndexError: list index out of range
Und die Anzeige links oben musst du noch anpassen, da fließen die Faktoren anscheinend auch mit ein wodurch es zu einer falschen Anzeige kommt.

Verfasst: Freitag 28. Juli 2006, 08:40
von DatenMetzgerX
hmmm Noch ein Fehler entdeckt :roll:

Bild

Das brachte ich zustande, in dem ich neue Punkte zeichnete, während er am fahren war.

Trage dich mal in die wxPython Maillinglist ein und frage dort nach.

Verfasst: Freitag 28. Juli 2006, 08:42
von gerold
JulesV hat geschrieben:Ich brauche wirklich hilfe - wie gesagt in einer Woche ist Abgabe und das Programm entscheidet maßgeblich darüber ob die letzten 4 Jahre meines Lebens komplett für die Katz waren...
Hi JulesV!

So tragisch würde ich das Ganze jetzt nicht sehen. :wink: Keine Schule oder Lehre kann wirklich umsonst sein, wenn man etwas dabei lernt.

So und jetzt zu deinem Programm:

1. Warum hast du unter die Steuerelemente keinen Panel gelegt? So kann man nicht mit der Tab-Taste weiter springen.

2. Warum kümmerst du dich nicht darum, dass dein Bild neu gezeichnet wird, nachdem das Fenster von etwas überdeckt wurde. Dafür gibt es das Paint-Ereignis. Aber es wird jetzt wohl zu spät für so eine korrektur sein.

3. Warum speicherst du die letzte Position deines Fensters nicht oder zentrierst es zumindest? Das ist bei so einem großen Fenster sicher keine schlechte Idee.

4. Warum gibt es keinen Schalter, der die Animation abbricht? Du beendest das komplette Programm.

5. Warum sperrst du nicht alle Steuerelemente, die während der Animation nicht angeklickt werden dürfen?

6. Warum arbeitest du mit einer fix vorgegebenen Höhe und Breite des Fensters? Wenn das schon sein muss, dann würde ich zumindest auch den Frame so einstellen, dass dessen Höhe und Breite nicht mehr verändert werden kann.

7. Timer sind bei kleinen Intervallen ungenau. Das ist bekannt und vom Betriebssystem abhängig. Für genauere Messungen und Steuerungen gibt es eigene **Echtzeitbetriebssysteme**.

Vielleicht bringt dich dieses kleine Beispiel trotzdem ein wenig weiter:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import time

FRAMES_PER_SECOND = 26
SECONDS = 3


framelength = 1.0 / FRAMES_PER_SECOND
counter_max = FRAMES_PER_SECOND * SECONDS

t_begin = time.time()
t_frametimer = t_begin
t_old = t_begin

counter = 0
t_old_diffs = []

while True:
    t = time.time()
    if (t - framelength) >= t_frametimer:
        t_frametimer_diff = t - t_frametimer
        t_old_diff = t - t_old
        print "%-16s  %-16s  %-16s" % (t, t_frametimer_diff, t_old_diff)
        t_frametimer = t_frametimer + framelength
        t_old = t
        t_old_diffs.append(t_old_diff)
        if len(t_old_diffs) > 1000:
            del t_old_diffs[0]
        counter += 1
        if counter == counter_max:
            break

print "Gesamtzeit:", time.time() - t_begin
print "Errechnete Rahmenlaenge:       ", framelength
print "Durchschnittliche Rahmenlaenge:",  sum(t_old_diffs) / len(t_old_diffs)
Alles kannst du nicht mehr ausbessern. Aber ein paar Dinge würde ich schon noch ändern, nachdem das Timer-Problem gelöst ist.

Viel Glück!

mfg
Gerold
:-)

Verfasst: Freitag 28. Juli 2006, 15:32
von JulesV
DatenMetzgerX hat geschrieben:hmmm Noch ein Fehler entdeckt :roll:

Das brachte ich zustande, in dem ich neue Punkte zeichnete, während er am fahren war.
Danke für den Hinweis. Dieses Verhalten ist neu und entsteht dadurch, dass ich erst die Strecke und den Hintergrund zeichne, in ein Bild speichere, und auf das Bild dann die Animation zeichne. Dadurch wollte ich die Zeit sparen, die beim Zeichnen der Strecke (die sich ja innerhalb eines Rennens nicht verändern sollte) entsteht. Beim setzen eines neuen Punktes wird also die Graphik der Strecke während der Animation nicht aktualisiert, aber das Auto fährt die neue Strecke trotzdem.
Das werde ich abschalten indem ich das Setzen neuer Punkte während der Animation nicht zulasse.

Verfasst: Freitag 28. Juli 2006, 15:48
von JulesV
gerold hat geschrieben: 1. Warum hast du unter die Steuerelemente keinen Panel gelegt? So kann man nicht mit der Tab-Taste weiter springen.
Ich hatte vorher noch nie mit wxwidgets gearbeitet und es hat sehr lange gedauert bis ich es überhaupt so hinbekommen habe. Auf die Idee es in ein Panel zu legen bin ich nie gekommen. Das mit den Tabs ist mir nie aufgefallen - aber ich denke auch dass es meinem Mathe-Professor nicht fehlen wird.
gerold hat geschrieben: 2. Warum kümmerst du dich nicht darum, dass dein Bild neu gezeichnet wird, nachdem das Fenster von etwas überdeckt wurde. Dafür gibt es das Paint-Ereignis. Aber es wird jetzt wohl zu spät für so eine korrektur sein.
Weil ich es unter OS X entwickelt hatte, und dort wird es offenbar automatisch neugezeichnet wenn es überdeckt wurde. Dies Verhalten unter Windows ist mir auch schon negativ aufgefallen aber ich habe noch keine Idee wie ich das beseitigen könnte. Hm, hast du eine Idee?
gerold hat geschrieben: 3. Warum speicherst du die letzte Position deines Fensters nicht oder zentrierst es zumindest? Das ist bei so einem großen Fenster sicher keine schlechte Idee.
Weil ich (noch) nicht weiß wie das geht :-). Ich hatte noch keine Zeit für diesen Feinschliff.
gerold hat geschrieben: 4. Warum gibt es keinen Schalter, der die Animation abbricht? Du beendest das komplette Programm.
Gute Idee, das werde ich einbauen!
gerold hat geschrieben: 5. Warum sperrst du nicht alle Steuerelemente, die während der Animation nicht angeklickt werden dürfen?
Das war in früheren Versionen sogar schon der Fall, aber seit dem musste ich mehrfach vieles umschreiben, so dass ich dies nachher dann vernachlässigt habe, weil das für mich zu den abschließenden Arbeiten zält. Das werde ich auf jeden Fall wieder machen!
gerold hat geschrieben: 6. Warum arbeitest du mit einer fix vorgegebenen Höhe und Breite des Fensters? Wenn das schon sein muss, dann würde ich zumindest auch den Frame so einstellen, dass dessen Höhe und Breite nicht mehr verändert werden kann.
Ich habe nicht herausgefunden wie ich das Fenster skalieren kann auf eine Weise, dass sich die Zeichenflächen und Elemente automatisch mit skalieren. Und ich habe auch nicht herausgefunden wie man die Höhe und Breite nicht mehr verändern kann. Ich bin selbst unzufrieden damit wie es jetzt ist...
gerold hat geschrieben: Vielleicht bringt dich dieses kleine Beispiel trotzdem ein wenig weiter
Ich werde es mir gleich mal probieren...

Verfasst: Freitag 28. Juli 2006, 15:56
von JulesV
Nirven hat geschrieben:Bei 40ms kommt da was weniger schönes raus, statt 2880000.0 ist der Faktor dann 3072546.23 (und einige Stellen mehr, aber das sollte nicht den großen Unterschied machen).
Erstmal vielen Dank!!
Ich frage mich ob ich so einen Nachkomma-Wert benutzen kann, ohne bei sehr langen Animationen in Probleme zu geraden, weil ja die Rundungsfehler sich quasi aufaddieren...
Nirven hat geschrieben:Da ist noch ein Problem in dem Programm, wenn es einmal rumgelaufen ist kommt sowas:
...IndexError: list index out of range...
Da habe ich spontan zwar noch keine Ahnung woran es genau liegt, (ist bei mir auch noch nicht aufgetreten), aber ich denke die Fehlermeldung spricht zum Glück eine deutliche Sprache :-)
Vielen Dank für diesen Hinweis!
Nirven hat geschrieben:Und die Anzeige links oben musst du noch anpassen, da fließen die Faktoren anscheinend auch mit ein wodurch es zu einer falschen Anzeige kommt.
Das ist allerdings ein Problem. Natürlich ist die Anzeige wieder abhängig vom Timer. Da der Timer unter Windows jetzt "schummelt" und die Sekunden quasi länger sind als sie es sein sollten, muss ich das wohl irgendwie rechnerisch ermitteln indem ich mit der Startzeit und den seit dem vergangenen Millisekunden rechne...

Also nochmal: VIELEN Dank... das war echt eine Lebensrettung :-)

Verfasst: Freitag 28. Juli 2006, 16:09
von JulesV
Nirven hat geschrieben:deine Konstante entsprechend anpassen (auf 4/5 reduzieren, also 2880000 statt 3600000).
Irgendwie komme ich noch nicht dahinter wie du auf die 4/5 kommst.
Und wie kommst du bei 40 ms auf 3072546.23 von 3600000?

Timerproblem doch nicht generell bei Win32

Verfasst: Freitag 28. Juli 2006, 22:04
von JulesV
Oh nein ... :-( ... ich habe den krummen Korrekturwert genommen, er funktioniert offenbar. Dann habe ich diverse andere Dinge die ihr hier vorgeschlagen habt eingefügt... jetzt läuft das Programm unter 2 von mir getesteten Windows-Kisten sehr gut. Unter OS X gibt braucht er manchmal ein paar Zehntelsekunden zu lang (nicht der Timer sondern die Animation), aber das macht nichts. Es muss eigentlich erstmal nur unter Windows laufen. Aber jetzt kommts: auf dem Notebook meiner Eltern (XP, SP1 und 700 MHz) läuft das Programm *zu kurz/schnell*! Dort darf ich den Korrekturwert also nicht mit einfließen lassen sondern muss doch wieder mit 3600000 rechnen.
Es ist wie gesagt ebenfalls XP (wie die anderen Testrechner).
Der einzige Unterschied den ich feststellen konnte war das SP1 anstatt SP2 - deshalb lade ich das gerade mal runter um es dann erneut zu probieren.

Da es also nicht an Windows oder nicht-Windows liegt sondern an anderen Faktoren, habe ich echt ein Problem mit der bedingten Kompilierung... :-(
Zumal ich nicht mal weiß an welchem Faktor es nun liegt ob der Timer funktioniert oder korrigiert werden muss.

Ich stelle meine aktuelle Programmversion gleich online, wieder unter:
http://www.schreibholz.de/jules/dateien/splinesim.zip

Verfasst: Samstag 29. Juli 2006, 01:12
von JulesV
Okay das Service Pack 2 spielt keine Rolle für das Funktionieren des Timers. Ich habe keine Idee mehr...

An den Versionen vom Interpreter usw. kann es nicht liegen, da ich es mit einer py2exe-Datei getestet habe, die ihre Libarys selbst mitbringt...

Verfasst: Samstag 29. Juli 2006, 08:37
von gerold
JulesV hat geschrieben:Ich habe keine Idee mehr...
Hi JulesV!

Kleiner Wink mit dem Betonpfeiler :-) --> Hast du mein kleines Beispiel schon ausprobiert? Es funktioniert ohne Timer. Ich denke mal, dass du es auf einem "NICHT Echtzeit Betriebssystem", mit einer "NICHT Echtzeit Programmiersprache" kaum genauer hin bekommen kannst.

mfg
Gerold
:-)

PS: Korrekturwerte? An so etwas würde ich gar nicht erst denken. :-(

Verfasst: Samstag 29. Juli 2006, 11:12
von gerold
Hi!

Ich habe jetzt Tabulatoren im Quellcode verwendet, damit du es besser in dein Programm integrieren kannst. Ich rate dir aber, vier Leerzeichen zum Einrücken zu verwenden, damit es für andere, die sich an den Standard halten, einfacher ist.


Aus dem Beispielcode, den ich ein paar Beiträge vorher geschrieben habe, habe ich eine HighResolutionTimer-Klasse erstellt. Diese Klasse arbeitet ähnlich wie der Timer von wxPython. Die HighResolutionTimer-Klasse löst nach einer einstellbaren Zeitspanne das Event EVT_ON_HIGHRESOLUTION_TIMER aus und sendet dieses Event an das mit dem Parameter "parent" angegebene WX-Objekt.


Der Timer muss vor jedem Start neu initialisiert werden. Das erledigt die Methode "initialize", an die man auch den Intervall in Millisekunden übergeben kann. Stellt man den Intervall zu klein ein, dann kommt der Computer mit der Fülle an Events evt. nicht mehr nach. Das war bei meinem Computer z.B. bei 10 ms der Fall.


Die Teile im Code, die ich verändert habe, habe ich mit mehreren Leerzeilen vom anderen Code abgegrenzt, damit du ihn besser erkennst. Dann muss ich dich noch darauf hinweisen, dass es inzwischen üblich ist, Events mit der Methode "Bind()" an ein Objekt zu binden. Damit erübrigen sich die vielen ID-Nummern. Und das Frame-Objekt hätte ich persönlich eher als Klasse programmiert, so wie du es z.B. mit "DrawPanel" gemacht hast.

Quellcode ausgelagert, um das Forum zu entlasten:
http://gelb.bcom.at/trac/misc/wiki/Pyth ... uellcode01

mfg
Gerold
:-)

PS: Habt ihr wirklich in der Schule gelernt, den Code so zu strukturieren? Ich finde, da ist einiges verbesserungswürdig. Aber dafür wirst du wohl keine Zeit mehr haben. Deshalb gehe ich nicht mehr genauer darauf ein.

Verfasst: Sonntag 30. Juli 2006, 15:51
von JulesV
Also vielen Dank für den Highresolutiontimer. Der funktioniert unter windows wirklich gut. (Unter OSX braucht er viel zu viel rechenleistung, so dass die Animation nicht hinterher kommt und er also zu lange braucht. Eine Idee warum?).

Wir haben nicht gelernt den Code so zu strukturieren. Aber wir haben allgemein kein Python gelernt und vor allem keine wxwidgets. Ich musste das lernen während ich es benutzte, und daher ist der Code so ein bisschen Stück für Stück gewachsen und hat sich teilweise an verschiedenen Quellen im Internet orientiert. Kann also daher kommen, dass es dir etwas komisch vorkommt wie er strukturiert ist.

So, erstmal vielen Dank
Jules

Verfasst: Sonntag 30. Juli 2006, 16:11
von gerold
JulesV hat geschrieben:Unter OSX braucht er viel zu viel rechenleistung, so dass die Animation nicht hinterher kommt
Hi Jules!

Versuche mal diesen Code unter Windows und unter osX. Vielleicht hat es schon genügt, ein kleines "sleep(0.02)" einzubauen.

Um das Forum zu entlasten, habe ich den geänderten Code hier hinterlegt:
http://gelb.bcom.at/trac/misc/wiki/Pyth ... uellcode02

mfg
Gerold
:-)