![]() |
Timer viel zu ungenau?
Hallo Freunde,
endlich funktioniert mein Kostenzähler mit automatischer Verbindungserkennung! :dancer: ABER: der zeigt die aktuelle verbindungsdauer an (mit einem Timer (1s)); vergleicht man die aber mal mit der anzeige der dfü-verbindung, stimmen sie schon nach ca. 2 minuten um mehrere sekunden NICHT überein.. das macht mehrere minuten aus in 2-3 stunden! gibt es irgendwie eine möglichkeit, etwas WIRKLICH JEDE SEKUNDE zu aktualisieren? vielleicht liegt das ja daran, dass noch zusätzlich die Rechenzeit mit einfließt, in der der timer den QT ausführt? ich zeig euch einfach meinen text:
Delphi-Quellcode:
type
Uhr = Record H : string; M : string; S : string; end; //Alle String, damit ich sie gleich nem Label zuw. kann var Satz : string := '0,89' //Kosten in ct/min ... procedure Tform1.Uhrzeit(var Zeit : Uhr; welche : integer); begin if StrtoInt(Zeit.S) >= 59 then begin if StrtoInt(Zeit.M) = 59 then begin Zeit.H := InttoStr(StrtoInt(Zeit.H) + 1); Zeit.M := '00'; if StrToInt(Zeit.H) > 2 then Label9.Font.Color:=clred; end; if StrtoInt(Zeit.M)<59 then Zeit.M:=InttoStr(StrtoInt(Zeit.M)+1); Zeit.S := IntToStr(StrToInt(Zeit.S) - 60); if welche = 1 then Label15.Caption := FloattoStr(StrtoFloat(Label15.Caption) + (StrtoFloat(Satz) / 100)); if welche = 2 then Label8.Caption := FloattoStr(StrtoFloat(Label8.Caption) + (StrtoFloat(Satz) / 100)); end; if StrtoInt(Zeit.S) < 59 then Zeit.S := InttoStr(StrtoInt(Zeit.S) + 1); if length(Zeit.H) = 1 then Zeit.H := '0' + Zeit.H; if length(Zeit.M) = 1 then Zeit.M := '0' + Zeit.M; if length(Zeit.S) = 1 then Zeit.S := '0' + Zeit.S; end; ... procedure TForm1.Timer1Timer(Sender: TObject); // Intervall = 1000 ms begin Uhrzeit(Jetzt,1); Uhrzeit(Heute,2); end; ein beispiel der differenz: DFÜ-Anzeige: 01:48:24 Online Meine Anzeige: 01:41:11 Online :cry: Hoffe, ihr könnt mir helfen! MfG Lefko. [edit=Daniel B]Delphi-Tags korrigiert. Mfg, Daniel B[/edit] |
Re: Timer viel zu ungenau?
Wenn ich das richtig sehe, dann addierst Du bei jedem Timer-Aufruf einfach eine Sekunde (=Intervall) zu Deiner Zeit. Leider darfst Du bei der Verwendung eines Timers nicht davon ausgehen, dass das Ereignis genau zu der gewünschten Zeit ausgelöst wird, genaugenommen ist 'Intervall' sowas wie eine Mindestzeit.
Du könntest aber z.B jedesmal, wenn der Timer feuert, mit GetTickCount die seit dem Windowsstart vergangenen Millisekunden messen und zu Deiner Zeit hinzuaddieren. Du musst natürlich beim Verbindungsaufbau die Startzeit messen und immer abziehen... Ciao, teebee |
Re: Timer viel zu ungenau?
Oder du machst da irgendwas mit
Delphi-Quellcode:
D.h. der Rechner wartet 1s mit deiner Schleife oder so. Ich denke, dass das etwas genauer ist.
sleep(1000);
MfG Florian :hi: |
Re: Timer viel zu ungenau?
Zitat:
Gruss, teebee |
Re: Timer viel zu ungenau?
Hi,
da ich dieses Thema schon mehrmals in eigenen Anwendungen hatte kann ich ganz sicher die von teebee vorgeschlagene Variante mit GetTickCount empfehlen. Man kann natürlich auch über Date oder Time gehen, aber für mich hat sich teebee's Methode als die Beste herausgestellt. Das mit dem Sleep ist keine gute Idee. Erstens verbraucht der Code für die Rechnerei auch Zeit, und so kommt nach jeder Sleep-Sekunde auch immer ein kleiner Offset dazu der sich über die Zeit addiert. Dann friert Sleep auch deinen Thread ein und solange du nicht extra für die Zeitmessung einen eigenen schreibst passiert das, was teebee sagte (Dein Thread steht und wartet eine Sekunde). Die Zeitabweichung bleibt aber immer noch. Gruß oki |
Re: Timer viel zu ungenau?
Ich würde da auch keinen Timer nehmen, ein Thread wäre da bestimmt besser.
Kannst ja zb mal auf Luckies Seite oder delphi-source.de kucken, da werden Threads zb erklärt. |
Re: Timer viel zu ungenau?
hi,
das genauste was ich bis jetzt kenne ist QueryPerformanceCounter in Verbindung mit QueryPerformanceFrequency... Frag mich aber nicht wie man das benutzt :? |
Re: Timer viel zu ungenau?
Ich glaube TeeBee's Posting ist nicht richtig verstanden worden.
Das Problem mit der Ungenauigkeit ist nicht das der Timer zu ungenau ist sondern das sich diese kleinen Abweichungen bei deiner Methode summieren. Das Problem liegt also in deiner Logik. Definiere eine globale Variabl Starttime: DWord := FetTickcount. BNun aktivierst du den Timer und jedesmal wenn das Event eintriffst berechnest du OnlineTime := CurrentTime - StartTime. Somit ist das Interval und die Genauigkeit des Timer egal, da nun die Onlinezeit vom Startzeitpunkt bis zum aktuelle Zeitpunkt gemessen wird. Deren genauigkeit liegt bei GetTickCount dann im Milisekunden Bereich. Gruß Hagen |
Re: Timer viel zu ungenau?
Hallo,
QueryPerformanceCounter benutzt man wie folgt:
Delphi-Quellcode:
Var
c, t1, t2 : int64; i : integer; begin QueryPerformanceFrequency(c); QueryPerformanceCounter(t1); { hier die zu messende Aktion } QueryPerformanceCounter(t2); i := 1000000 *(t2 -t1) div c; ShowMessage(IntToStr(i) +' Mikrosekunden'); end; |
Das genügt!
Hallo Freunde,
danke für die ganzen Tipps, ich mach s jetzt am besten mit der Onlinetime := now - Starttime :dancer: MfG Lefko. :coder: |
Re: Timer viel zu ungenau?
@MrSpock, am genauesten ist heutzutage der Real Time Counter, RDTSC. Dieser wird mit jedem Takt der externen CPU Taktfrequenz erhöht. Alle neueren CPU's unterstützen diesen:
Delphi-Quellcode:
Falls dich noch die Umwandlung von Cycles in Millisekunden bzw. Nanosekunden interessiert, poste ich sie.
function IsRDTSCPresent: Boolean; assembler;
// check is CPUID Instruction present // extracted from my DEC Part I (Copyright), and litte bit expanded to support TSC Flag directly asm PUSHFD PUSHFD POP EAX MOV EDX,EAX XOR EAX,0040000h PUSH EAX POPFD PUSHFD POP EAX XOR EAX,EDX JZ @@1 PUSHFD POP EAX MOV EDX,EAX XOR EAX,0200000h PUSH EAX POPFD PUSHFD POP EAX XOR EAX,EDX @@1: POPFD TEST EAX,EAX JZ @@2 MOV EAX,1 DW 0A20Fh // CPUID TEST EDX,010h // test RDTSC flag in Features SETNZ AL @@2: end; function RDTSC: Int64; // sollte UInt64 sein asm DW 0310Fh // RDTSC Opcode end; procedure Test; var Start,Cylces: Int64; begin if IsRDTSCPresent then begin Start := RDTSC; DoAnything; Cycles := RDTSC - Start; end; // Cycles ~ Taktzyklen der CPU die DoAnything benötigt hat end; Gruß Hagen |
Re: Timer viel zu ungenau?
Her damit. Kommt in die Code-Lib.
|
Noch ne Frage
Hi,
so, das funktioniert gut mit dem OnlineZeit := now - Startzeit; ABER: (Es gibt immer ein aber... :? ) Wie kann ich denn zwei Zeitwerte addieren? der Tageszähler hat ja schon nen wert, auf den die jetzige onlinezeit noch draufaddiert werden soll! Ich hab das folgendermaßen gemacht, aber es klappt nicht ganz:
Code:
was is daran falsch? :roll:
Label9.Caption := Ini.ReadString('Kosten','Tagesz','00:00:00');
Tageszeit := StrToTime(Label9.caption); Onlinezeit := now - Startzeit; Label9.caption := TimeToStr(Tageszeit + Onlinezeit); Edit: Kommando zurück, hab voll den doofen denkfehler gemacht und zwar an ner ganz anderen stelle.. sorry, alles bestens :party: MfG Lefko. |
Re: Timer viel zu ungenau?
Liste der Anhänge anzeigen (Anzahl: 1)
@Lucki, dein Arbeitseifer wird mir langsam unheimlich, ich kann doch nicht jeden meiner Beiträge in die Codelib stellen :)
Also gut, erstmal der Source, dann die Erklärung:
Delphi-Quellcode:
Ok, IsRDTSCPresent erkärt sich von selber, es testet ob die CPU alle nötigen Features unterstützt und ob das OS diese auch für uns "freigeschaltet" hat.function RDTSC: Int64; // sollte UInt64 sein // Liest den Time Stamp Counter der CPU asm DW 0310Fh // RDTSC Opcode, hier als DW für D3-D4 end; function IsRDTSCPresent: Boolean; // Überprüft ob der Time Stamp Counter durch die CPU unterstützt wird. // Extrahiert aus meinem Delphi Encryption Compendium. Es gelten die // Copyright aus dem DEC, Public Domain. function HasRDTSC: Boolean; assembler; asm PUSH EBX PUSHFD PUSHFD POP EAX MOV EDX,EAX XOR EAX,0040000h PUSH EAX POPFD PUSHFD POP EAX XOR EAX,EDX JZ @@1 PUSHFD POP EAX MOV EDX,EAX XOR EAX,0200000h PUSH EAX POPFD PUSHFD POP EAX XOR EAX,EDX @@1: POPFD TEST EAX,EAX JZ @@2 MOV EAX,1 DW 0A20Fh // CPUID TEST EDX,010h // test RDTSC flag in Features SETNZ AL @@2: POP EBX end; begin // dieser Try Except Block ist absolut nötig. // RDTSC kann eine privilegierte Instruktion sein, d.h. das OS kann jederzeit // so konfiguriert sein das es die CPU anweisst das RDTSC eine priviligierte // Instruktion ist. try Result := HasRDTSC; if Result then RDTSC; except Result := False; end; end; function CalcCPUFrequency(Rounds: Cardinal = 1): Int64; // Berechnet die CPU Taktfrequenz. Diese Funktion nutzt eine sehr exakte und schnelle Methode. // Relativ zu einem Referenztakt werden die Taktzyklen der CPU gezählt. // Danach wird über unseren Referenztakt und dessen Frequenz die Taktzyklen in // die CPU Taktfrequnz umgerechnet. Die genaueste Referenzquelle im Windows System // ist QueryPerformaceCounter() + QueryPerformanceFrequncy(). Beide werden durch // den Real Time Clock Chip der mit dem BIOS zusammenarbeitet erzeugt. // Auf den meisten Systemen arbeitet dieser mit einem Takt von 3.579.545 Hz = 3.6 MHz. // D.h. wir können mit dieser Funktion die CPU Taktfrequenz mit einer maximalen // Genauigkeit von 3.6 MHz errechnen. Sollte die CPU mit 1500MHz getaktet werden so // beträgt die best mögliche Genauigkeit +- 1500MHz/3.6MHz = +-417 Hz. // Die Meßmethode selber ist unabhänig vom Tasksheduler von Windows da relativ zu // zwei Frequenzen die unabhänig von Tasksheduler sind gerechnet wird. // D.h. die Länge der Meßdauer ist im Grunde unwichtig und kann sehr kurz gehalten werden. // Warum beschreibe ich das ?? // Weil es im WEB viele Sourcen gibt die eine Meßschleife per Sleep() oder GetTickCount() // aufbauen. Beide Methoden sind abhängig vom Tasksheduler und haben eine viel zu geringe // Genauigkeit. Die bestmögliche Genauigkeit mit GetTickCount() und einer 1.5GHz CPU // liegt bei 1.500.000.000Hz / 1.000Hz = +-1.500.000 = +-1.5MHz. D.h. die Auflösung // mit GetTickCount = 1ms = 1000Hz ist 1.500.000 / 417 = 3.597 mal schlechter als // mit nachfolgender Methode. Die Auflösung bei Sleep() liegt bestenfalls bei 10ms, // also 10 mal schlechter als mit GetTickCount(). // Natürlich wird die theoretische Genauigkeit bei einer 1.5GHz CPU von +-417Hz nicht // erreicht. Im Durchschnitt liegt sie jedoch bei +- 2000Hz. // Rounds erhöht die Genauigkeit, 100 macht es ~100 mal genauer, aber nur hypothetisch. // Probleme könnten mit den Int64 auftreten falls die CPU schon sehr lange läuft. var C,F,S,E,D,T: Int64; begin if IsRDTSCPresent and QueryPerformanceFrequency(F) and QueryPerformanceCounter(S) then begin C := F * (Rounds +1); QueryPerformanceCounter(S); D := RDTSC; while C > 0 do Dec(C); QueryPerformanceCounter(E); T := RDTSC; Result := Round((T - D) * F / (E - S)); end else Result := 0; end; var Frequency: Int64 = 0; function CPUFrequency: Int64; // gibt die Takzyklen pro Sekunde zurück begin if Frequency = 0 then begin Frequency := CalcCPUFrequency; if Frequency = 0 then raise Exception.Create('Kann CPU Frequenz nicht berechnen'); end; Result := Frequency; end; function Secs(Cycles: Int64): Double; // rechnet Taktzyklen in Sekunden um begin Result := Cycles / CPUFrequency; end; function Ticks(Cycles: Int64): Double; // rechnet Taktzyklen in Millisekunden um begin Result := Cycles * 1000 / CPUFrequency; end; procedure Test; var Start,Stop: Int64; Tick: DWord; begin WriteLn('CPU Taktfrequenz ist ', CPUFrequency/1000000.0:6:1, ' MHz'); Tick := GetTickCount + 100; Start := RDTSC; while GetTickCount < Tick do ; Stop := RDTSC; WriteLn; WriteLn('Testschleife dauerte: '); WriteLn('Taktzyklen : ', Stop - Start:10); WriteLn('Millisekunden : ', Ticks(Stop - Start):10:2); WriteLn('Sekunden : ', Secs(Stop - Start):10:2); end; // diese Source ist Public Domain, Hagen Reddmann at Negah Am wichtigsten ist die Ermittlung wieviele Takte pro Sekunde nun die CPU ausführen kann, d.h. die Taktfrequenz der CPU wird benötigt. Mit CalcCPUFrequency wird dies erledigt. Um ein maximal exaktes und schnelles Ergebnis zu bekommen ist es wichtig mit welcher Referenzquelle man arbeitet. Auf Windows-Systemen ohne spezielle Hardware stehen uns GetTickCount(), Sleep() und QueryPerformanceFrequency() zur Verfügung. Sleep() arbeitet im zehntel Millisekunden Bereich genau, und stellt eigentlich keine Referenzquelle dar da deren Frequenz eben ungleichmäßig ist (das Multithreading funkt dazwichen). GetTickCount arbeitet auf 1ms genau, deren Taktfrequenz ist 1000 Hz. Am genauesten ist QueryPerformanceFrequency() die abhänig vom System ca. 3.6 MHz ist. Diese arbeitet mit dem RTC (Real Time Clock Chip) der teilweise zur Hardwareausstattung eines BIOS gehört. Wir ermitteln unsere Referenzfrequenz in F. Unsere Meßschleife sollte nun länger als 3.6 MHz Taktzyklen dauern um keine groben Meßfehler durch Interferenzbildung bei zu geringer Abtastrate zu erhalten. Wir ermitteln einmal in S den Startzeitpunkt mit QueryPerformanceCounter() und in D in Taktzyklen. Nun warten wir eine weile und ermitteln unsere Stopzähler in E und T. Um nun die Taktfrequnz zu ermitteln führen wir eine einfache Verhältnisgleichung durch, wie im Matheunterricht gelernt (tja hat doch was gebracht :) Also E - S = Dauer in QueryPerformanceCounter, T - D = Dauer in Taktzyklen, somit (T - D) * F / (E - S) = Taktfrequenz. Gruß Hagen |
Re: Timer viel zu ungenau?
Zitat:
Zitat:
Öhm, ich mußte in den Projektoptionen "Zuweisbare typisierte Konstanten" aktivieren, sonst gab es hier:
Delphi-Quellcode:
einen Fehler: Rechter Seite kann nicht zugewiesen werden. Kann man das noch ausbügeln?
Frequency := CalcCPUFrequency;
|
Re: Timer viel zu ungenau?
Jo schade, hier kommen wir an einen Punkt der mich am neuen Delphi Compiler ärgert. Ich finde die Deklaration von privaten aber globalen initialisierten Variablen echt sauber, denn Frequency soll global sein, aber nur lokal in CPUFrequency sichtbar.
Man kann zwar im D7 einen Compilerswitch stetzen (WRITABLECONST ON} oder so, aber ich habe nun Frequency als Unit Lokale Var deklariert. Damit wird sie eben auch global zu allen nachfolgenden Unit Proceduren, aber was soll's. Gruß Hagen |
Re: Timer viel zu ungenau?
Da wünscht man sich dann eine static Deklaration, wie unter C++. :?
|
Re: Timer viel zu ungenau?
Richtig, und die lokale initialisierte Const Deklaration ist für mich dieser static !
Man sucht sich ja doch aus allen Sprachen die man so beherrscht die besten Features raus :) Ein
Delphi-Quellcode:
Würde mir als Ersatz schon reichen.
procedure XYZ;
var MyStatic: Integer = 0; begin end; Gruß Hagen |
Re: Timer viel zu ungenau?
Wie war das? Neues Thema, neuer Thread? :dancer:
MfG Lefko |
Re: Timer viel zu ungenau?
Ich hab da jetzt aber doch noch ein problem:
ich hab ein label, in dem die schon vorhandenen kosten stehen, etwa so: "4,5215€ diesen Monat". Nun soll immer, wenn die onlinezeit (---> onlinezeit := now - startzeit) um eine minute erhöht wird, der satz ( € / Min) addiert werden; WIE SOLL DAS GEHEN? Ich komme auf keinen richtigen ansatz, nur mist! ich könnte eine variable "minuten" haben, die dann bei starttime auf 0 gesetzt wird und dann prüft der timer immer, ob "onlineminuten" (mit decodetime(onlinzeit,...) = "minuten" ist; wenn nicht, dann wird minuten := onlineminuten gesetzt und der satz zu dem label addiert. mensch, wärend ich den post schreibe fällt mir ne möglichkeit ein, lol :dancer: öh, hat jemand ne bessere idee? :roll: MfG Lefko. |
Re: Timer viel zu ungenau?
Ist doch ziemlich einfach. Neben der aktuellen Online Startzeit und der aktuellen Zeit die ja dann die abgelaufene Online Zeit darstellen hast du noch die letzten Kosten die aufgelaufen sind. Nun die aktuelle Onlinezeit in Minuten * die Kosten pro Minute auf den letzten gemessenen Moantsbetrag drauf addieren. Wenn der User Offline geht addierste die kompletten Kosten der aktuellen Onlinesession auf den letzen Monatswert drauf.
Gruß Hagen |
Re: Timer viel zu ungenau?
Zitat:
ich hab s jetzt so gemacht, aber ob das so gut is? :
Delphi-Quellcode:
MfG Lefko.
//Zeit- und Kostenberechnung
Onlinezeit := now - Startzeit; //Heute Label9.Caption := TimeToStr(Tageszeit + Onlinezeit); //Monat Label11.Caption := TimeToStr(Monatszeit + Onlinezeit); //Jetzt Label13.Caption := TimeToStr(Onlinezeit); if copy(Label13.caption,7,2) = '00' then //wenn 'ne neue minute beginnt begin Label15.caption := FloatToStr(StrToFloat(Label15.caption) + StrToFloat(Satz) / 100); Label8.caption := FloatToStr(StrToFloat(Label8.caption) + StrToFloat(Satz) / 100); Label7.caption := FloatToStr(StrToFloat(Label7.caption) + StrToFloat(Satz) / 100); end; [edit=Daniel B]Delphi-Tags korrigiert. Mfg, Daniel B[/edit] |
Re: Timer viel zu ungenau?
Hi
deine ständige Umrechnungen mit StrToFloat() sind unschön:
Delphi-Quellcode:
Gruß Hagen
begin
KostenProTag := KostenProMinute / 1440; OnlineZeit := Now - Startzeit; OnlineKosten := OnlineZeit * KostenProTag; MonatsZeit := MonatsZeitVorOnlineStart + OnlineZeit; MonatsKosten := MonatsKostenVorOnlineStart + OnlineKosten; Label9.Caption := FormatDateTime('hh:nn:ss', OnlineZeit); Label11.Caption := FormatDateTime('dd Tage hh:nn:ss', MonatsZeit); // aktuelle Kosten der aktuelle Onlinezeit Labelxx.Caption := FormatFloat('0.00#,## €', OnlineKosten); // kumulierte Monatskosten Labelxy.Caption := FormatFloat('0.00#,## €', MonatsKosten); .. usw. usw. end; |
Re: Timer viel zu ungenau?
Zitat:
|
Re: Timer viel zu ungenau?
Korrekt: KostenProTag := KostenProMinute * 1440 ist richtig.
Du arbeitest mit TDateTime. Ein TDateTime ist eine Fließkommazahl die im Ganzzahlteil der Zahl, also vor dem Komma die Tage angibt. Im Gebrochenen Teil der Zahl also Nachkommateil, werden die Bruchteile eines Tages angegeben. Nun (KostenProMinute * 1440) = KostenProTag, KostenProTag * Bruchteile eines Tages = Kosten. Somit sind die KostenProTag rechenkompatibel mit einem TDateTime. Man kann also direkt rechnen Kosten := (Now - StartDatumZeit) * KostenProTag. Vorteil bei der Sache ist das wenn man mehrere Tage und Stunden online ist obige Formel trotzdem richtig rechnet. So einfach :) Probier mal
Delphi-Quellcode:
Gruß Hagen
KostenProTag := 0.10 * 1440; // 10 Cent pro Minute
Kosten := (StrToDateTime('15.1.2003 18:34') - StrToDateTime('10.1.2003 09:12')) * KostenProTag. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:56 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz