Countdown accuracy

SandroB4A

Member
Licensed User
Hi to all,

I've made a simple countdown code and what I want is something like:

Input: Time intervals e.g. 3,5,4 sec.

Output:

begin of phase 1
3
2
1
begin of phase 2
5
4
3
2
1
begin of phase 3
4
3
2
1

and so on...easy right?


When I tried to test bigger intervals (some minutes) I've noticed that some number was skipped, e.g.

....
239
238
236
235
...

So I supposed was a code accuracy problem and I've changed the output in millisec. I expected something like:

3000
2000
1000
...

This is what I got:

Begin of Phase:0
3000 ‘Value not really calculated
1961
945

Begin of Phase:1
5000 ‘Value not really calculated
3939
2925
1883
831

Begin of Phase:2
4000 ‘Value not really calculated
2954
1935
926

Above numbers rounded in seconds are more or less correct, but not always.

So, finally, my question: Where I'm wrong? There's a better way to code a countdown as I need?

Following the code I used:


B4X:
#Region  Project Attributes 
   #ApplicationLabel: CountDown
   #VersionCode: 1
   #VersionName: 
   #SupportedOrientations: unspecified
   #CanInstallToExternalStorage: true
#End Region

#Region  Activity Attributes 
   #FullScreen: False
   #IncludeTitle: False
#End Region

Sub Process_Globals
End Sub

Sub Globals
   Dim T1 As Timer
   Dim Dur As Long         'Single phase duration
   Dim TInit, TFinal,DT As Long   
   Dim PhaseList() As Long
   Dim PhaseNum As Int      : PhaseNum=0
   Dim tick As Int                 : tick=1000
End Sub

Sub Activity_Create(FirstTime As Boolean)   
   T1.Initialize("T1",tick)

   PhaseList=Array As Long(3,5,4)    'For testing, these values will be fetched from a DB [sec]

   TInit=DateTime.now            'Save the start moment
   Dur=1000*PhaseList(PhaseNum)      'Calc the interval in millsec
   TFinal=TInit+Dur            'Calc the phase finish moment
   
   Log("Begin of Phase:"&PhaseNum)
   Log(Dur)                  'Show the first value
   T1.Enabled=True   
End Sub

Sub T1_tick
   If DateTime.Now<TFinal Then
      DT=TFinal-DateTime.Now
      Log(DT)      'Visualize the time remaining in millsec
   Else   
      PhaseNum=PhaseNum+1      'Pass to the next phase

      If PhaseNum>PhaseList.Length-1 Then
         T1.Enabled=False      'Stop the countdown
      Else
         Dur=1000*PhaseList(PhaseNum)
         TInit=DateTime.now      'Reset Initial time
         TFinal=TInit+Dur      're-calc final time         
         Log("Begin of Phase:"&PhaseNum)
         Log(Dur)            'Show the first value

      End If      
      
   End If
End Sub

Sub Activity_Resume   
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Tks in advance:D
 

Erel

Administrator
Staff member
Licensed User
Timers are not 100% accurate. They cannot be 100% accurate as they run in the main thread.

The solution (which I believe is similar to what you did) is to use a shorter interval and check the system clock:
B4X:
Sub Process_Globals
   Dim timer1 As Timer
   Dim target As Long
End Sub

Sub Globals

End Sub

Sub Activity_Create(FirstTime As Boolean)
   If FirstTime Then
      timer1.Initialize("timer1", 100)
   End If
   StartCountDown(10)
End Sub

Sub StartCountDown (Seconds As Int)
   target = DateTime.Now + DateTime.TicksPerSecond * Seconds
   timer1.Enabled = True
End Sub

Sub Timer1_Tick
   Dim seconds As Int = Round((target - DateTime.Now) / DateTime.TicksPerSecond)
   Log(seconds)
   If seconds <= 0 Then
      timer1.Enabled = False
   End If
End Sub
 
Upvote 0

SandroB4A

Member
Licensed User
Thanks for your reply Erel!

Ok, the absolute precision isn't possible (off course), so the problem is mainly of visualization.

I tested your code with a ten minute interval and no numbers were skipped. I will use your scheme and also DateTime.TicksPerSecond constant instead of explicit value.
 
Upvote 0
Top