problem calculating time

Blitz3D Forums/Blitz3D Programming/problem calculating time

FiNegirO(Posted 2006) [#1]
Hi!
Imagine a 2d square area, divide it in 4 quadrant and let a player move in the area for 1 minute. How is the best way to calculate the time the player spent in each quadrant when the time is out?

I can’t get good results. Here is the code I’m using in the main loop:



The results (Latency#, TimeIResult#,…) I get using this code are wrong. The latency# should be 60.0 but I always obtain bigger results (like 60.012) . The sum of the 4 TimeXResult# gives the same number (i.e. 60.012).

Is it a conversion type problem? Or the lost of precision is due to the mathematic operations?
Any suggestions to do it better? I need “millisec” precision.

Thanks a lot

FiNegirO


markcw(Posted 2006) [#2]
hi, i tested this out and it looks like some commands, i think the ones related to drawing, eat up a few milliseconds, in particular i found flip was the most noticable, also cls with text occasionally caused a small timing error.

Maybe if you give the timer job to a dll it would allow you to have this accuracy. Here is the code i used.

Graphics3D 640,480,0,2

iniTime=MilliSecs()

While Not KeyHit(1)

 tempTime=MilliSecs()-iniTime
 If MilliSecs()>=iniTime+1000 ;1 second test
  If once=0
   once=1
   Latency#=(MilliSecs()-iniTime)/1000.0
   testTime=MilliSecs()-iniTime
   testFloat#=1000/1000.0
  EndIf
 EndIf

 ;If once=1
  Cls
  Text 0,0,"Latency="+Latency+" testFloat="+testFloat
  Text 0,12,"testTime="+testTime+" iniTime="+iniTime+" tempTime="+tempTime
 ;EndIf

 If once=1 Then Flip

Wend



b32(Posted 2006) [#3]
Edit:
The alternative would be checking the time more often.
Here is my version, it does exactly the same:

/edit


markcw(Posted 2006) [#4]
i think now what i posted above isn't quite correct, as the error margin of 1 sec and 60 sec is the same. so it must be more like flip, etc. causes a read error/delay with the current millisecs. the solution then shouldn't need a dll, but just not calling flip on the loop before you get millisecs.


FiNegirO(Posted 2006) [#5]
Thanks muk and bram32.
I tried your code and I get the same errors as in mine.

bram32, do your code works for you? I modified it just to display the errors between Total time and Total sum. I can't explain the results: Total sum is usually more then the wanted time and Total time is more than Total sum! Here is the code (thanks bram32):

	Graphics 800, 600, 0, 2
	SetBuffer BackBuffer()
	
	Dim time#(4)
	
.up	

	For i = 0 To 3
		time#(i) = 0.0
	Next

	
	init = True
	
	Repeat
	
		;calculate time passed
		now# = MilliSecs()
		If init Then prev# = now#: inittime# = now: init = False
		elapsed# = now# - prev#
		prev# = now#
		
		;select quadrant
		mx = MouseX()
		my = MouseY()
		quad = (mx > 400) + (my > 300) * 2
		;set time for selected quadrant
		time(quad) = time(quad) + elapsed
		
		;graphical output
		Cls
		
		;draw grid
		Line 400, 0, 400, 600
		Line 0, 300, 800, 300
		
		;print out times
		For i = 0 To 3
			Text i Mod 2 * 400 + 200, i / 2 * 300 + 150, time#(i) / 1000.0
		Next
				
		;measure total time passed
		tot1# = ((time(0) + time(1) + time(2) + time(3)) / 1000.0)
		tot2# = (MilliSecs() - inittime#) / 1000.0

		;debug output		
		Text 0,  0, "You're in quadrant: " + quad
		Text 0, 20, "Total sum times:      " + tot1#
		Text 0, 40, "Total time millisecs: " + tot2#
		Flip

		;limit to 60 secs			
		If tot2# >= 3.0 Then 
			Err1# = tot2# - 3.0
			Err2# = tot2# - tot1#
			Text 0, 60, "Error1 (Total time - 3 sec.): " + Err1#
			Text 0, 80, "Error2 (Tot time - Tot sum):  " + Err2#
			Flip
			Exit
		EndIf
		
	Until KeyHit(1)
	
	Text 400, 290, "Press space to retry", True, True
	Text 400, 310, "Any other key to exit", True, True
	Flip
	
	Key = WaitKey()
	If Key = 32 Then Goto up
		
	End



The other strange thing is, as muk said, the error/delay is the same for 1 sec, 60 sec or more. I'm not sure to understand your solution muk. I need to flip continuously because of the graphics. How should I wait to get millisecs without flipping?

FiNegirO


b32(Posted 2006) [#6]
Floats behave a bit strange:
x# = 0

For i = 1 To 1000
x# = x# + 0.0001
Next

Print x

WaitKey()
End

They are only as exact as they need to be. In the code above, it is precise until 0.0001 digits. All the stuff after that is just "rubbish". I believe it has something to do that inside the CPU floats are just integers as well.

Then in the graphical routine, it takes time to render each frame. So if you check the Millisecs() once a loop, it will not check every millisecond. Say the program renders at 30 FPS, then the loop repeats at 30FPS, so the "exit timer" checks at 30FPS, too. There is a deviation of one frame allways. The only thing you can do about this is check the exit timer more often.


FiNegirO(Posted 2006) [#7]
Thanks for the clarification! But I don’t understand how could I check the exit timer more often while rendering the scene? Could you give me an example please?

FiNegirO


b32(Posted 2006) [#8]
Well, you could for instance check the timer after each line of code.
x# = cos(p) * 10: CheckTimer()
y# = sin(p) * 10: CheckTimer()
oval x, y, 10, 10: CheckTimer()
for i = 0 to 10
  oval rand(x), rand(y), 10, 10: CheckTimer()
next

If you want to render in several steps, you could use a Type for all your objects.
For i.TObject = Each TObject
  ShowEntity i\mesh
  RenderWorld()
  HideEntity i\mesh
  CheckTimer()
Next

In fact, using threads does the same thing. Only then with two applications. Each application gets a certain amount of processor time.

There is a wait built in the "Flip" command. The VWait waits for the ScanLine() to be zero. It avoids nasty flickering of the image.

During this wait, other application can perform their tasks. Same thing goes for Delay().
To get as close to the millisecs() accuracy, you should have:
* a tight loop
* check very often



markcw(Posted 2006) [#9]
i guess the simplest way is like this, ie. don't flip the 15 millisecs before getting your time result, 20 is the margin of error.

Graphics3D 640,480,0,2

iniTime=MilliSecs()

While Not KeyHit(1)

 tempTime=MilliSecs()-iniTime
 If MilliSecs()>=iniTime+1000 ;1 second test
  If once=0
   once=1
   Latency#=(MilliSecs()-iniTime)/1000.0
   testTime=MilliSecs()-iniTime
   testFloat#=1000/1000.0
  EndIf
 EndIf

 Cls
 Text 0,0,"Latency="+Latency+" testFloat="+testFloat
 Text 0,12,"testTime="+testTime+" iniTime="+iniTime+" tempTime="+tempTime

 If MilliSecs()>=iniTime+1000-15 And MilliSecs()<=iniTime+1000+5
 Else
  Flip
 EndIf

Wend



b32(Posted 2006) [#10]
ah, yes that should work perfectly!


FiNegirO(Posted 2006) [#11]
Ok!
Timing is better (still not perfect) but what happens with the graphics?

Try this:

Graphics3D 640,480,0,2


camera=CreateCamera()
light=CreateLight()
RotateEntity light,90,0,0
cube=CreateCube()
PositionEntity cube,0,0,5


iniTime=MilliSecs()
OneSecondTime = iniTime

While Not KeyHit(1)

 tempTime=MilliSecs()-iniTime
 If MilliSecs()>=OneSecondTime+1000 ;1 second test
  Latency#=(MilliSecs()-OneSecondTime)/1000.0
  testTime=MilliSecs()-OneSecondTime
  OneSecondTime = MilliSecs()
  testFloat#=1000/1000.0
 EndIf

 UpdateWorld
 RenderWorld
 
 Text 0,0,"Latency="+Latency+" testFloat="+testFloat
 Text 0,12,"testTime="+testTime+" iniTime="+iniTime+" tempTime="+tempTime
 ;Text 50,50,"" + MilliSecs() Mod 1000

 TurnEntity cube,-.4,.2,.3

 If (MilliSecs() - OneSecondTime) Mod 1000 < 985 And (MilliSecs() - OneSecondTime) Mod 1000 > 5 
  Flip ;flip only if "far" from time checking
 EndIf

Wend


Could you see the gap in the render process? It's a big problem in a real time game.

Any idea to solve it?

Thanks a lot,
FiNegirO


markcw(Posted 2006) [#12]
the main problem there is you're updating the cube every loop so it's not syncing with the flip and causing that big jump. the solution is to group any graphics code in with the flip as well.

i changed your mod thingy as it gave some other kind of problem and i couldn't understand how to fix it.

Graphics3D 640,480,0,2
SetBuffer BackBuffer()

camera=CreateCamera()
light=CreateLight()
RotateEntity light,90,0,0
cube=CreateCube()
PositionEntity cube,0,0,5

iniTime=MilliSecs()
nextTime = iniTime

While Not KeyHit(1)

 tempTime=MilliSecs()-iniTime
 If MilliSecs()>=nextTime+1000 ;1 second test
  Latency#=(MilliSecs()-nextTime)/1000.0
  testTime=MilliSecs()-nextTime
  nextTime = MilliSecs()
  testFloat#=1000/1000.0
 EndIf

If Not MilliSecs()>=nextTime+1000-30 And MilliSecs()<=nextTime+1000+20

 UpdateWorld
 RenderWorld
 
 Text 0,0,"Latency="+Latency+" testFloat="+testFloat
 Text 0,12,"testTime="+testTime+" iniTime="+iniTime+" tempTime="+tempTime
 ;Text 50,50,"" + MilliSecs() Mod 1000

 ;If (MilliSecs()-nextTime) Mod 1000<985 And (MilliSecs()-nextTime) Mod 1000>5 
 TurnEntity cube,-.4,.2,.3

 Flip ;flip only if "far" from time checking
EndIf

Wend



FiNegirO(Posted 2006) [#13]
Thanks muk! It worked for me. Now I get better timing without graphics gaps. Still there are some time errors. I'm trying to do time check more often, as bram32 said. I hope to get "almos perfect" results. I'll tell you.

cheers
FiNegirO


b32(Posted 2006) [#14]
Hey, I was thinking .. maybe you could play a .WAV that is exactly 1 minute and use Channelplaying ?
edit: No, that would be the same actually.. Nevermind that. I was just thinking about the .wav playing as an independant process.