Belgium
Asked — Edited

Comparing Times In Ezscipt

Im struggling with using time in ezscript. I want to check how long it has been since a given face was recognized. But all the time functions seem to give strings and I cant really figure out how to convert those to number and compare. Using the script help I came up this this:

Quote:

$lastfacedetection= now() ....

if ( FmtTimeSpan(CDateTime(now()) - CDateTime($lastfacedetection) ), "mm") >5)) do something endif

but that doesnt work, it tells me: Operator '-' invalid for strings.

Help?


ARC Pro

Upgrade to ARC Pro

Unleash your creativity with the power of easy robot programming using Synthiam ARC Pro

PRO
Synthiam
#1  

In the past, people would calculate the hours, minutes, seconds manually and it would be a lengthy equation. I always assumed the FmtTimeSpan() would do the trick, since the EZ-Script datetime format and functions derive directly from their .Net counterparts. I looked and to my surprise, there is no way to retrieve total minutes or total seconds, etc from a timespan format! That really surprised me.

So the new release includes TotalHours(), TotalMinutes() and TotalSeconds() functions from timespan: https://synthiam.com/Community/Questions/11811

You can use them like this...


# get the date of 24 hours ago
$startDate = AddDays(Now(), -1)

# get the total number of minutes between the two dates
$minutes = TotalMinutes(now() - CDateTime($startDate))

print($minutes)

Belgium
#3  

So on a Sunday, 3 hours after I posted a question, I get not only a response, but a new release that fixes a problem I ran in to? Thats impressive:).

I''ll give it a try next week, thanks a lot.

Btw another issue Im running in to frequently and that is slightly annoying, is that I cant keep a script open in one window and do something in another window. The ezscript editor insists on staying in focus. I imagine window management could get slightly messy/confusing if you allow multiple script windows to stay open within the same ARC window,, but it is often useful, like when I need to look up some servo position or test something in another script or copy paste between scripts.

To keep it manageable for people who dont often need this, maybe you could add an option to detach a script editor so it becomes its own windows... , well, window? Similarly, I would love to make better use of my display screen estate. I have 4 monitors, not counting my laptop, I know, you may laugh, but I can only use one, and it feels really cramped with ARC GUI and I keep having to scroll and/or switch "workspaces". Anything you can think off to make that more efficient would be appreciated.

Belgium
#4  

Im still having some issues, this time with precision. Consider this code:


$tmp= now()
$delta=TotalSeconds(now()-CDateTime(($tmp)))
print($delta)

Since this code does nothing except save current time and then compare it to the current time, you would expect to get zero, or at least really close to zero since the script executes in milliseconds.

Instead I get a random number between 0 and 1 (seconds). Probably because CDateTime rounds to a second even though the internal clock and now() work at a higher precision.

Is there a way to store decimal seconds or milliseconds from the clock?

PRO
Synthiam
#5  

You are not getting random numbers. You are getting the difference between Now() and Now() on two different command executions.

Think about how code works... it reads line by line and executes one line in front of another.

So, this puts the current time into the variable $tmp $tmp = now()

a few clock cycles go by and you compare the current time with the time that was assigned to the variable

$delay = TotalSeconds(now() - CDateTime($tmp))

Computers execute commands in a consecutive linear fashion, that means one after another.

To give you an example that is testable - try this modified version of your code example..


# GET THE CURRENT TIME
$tmp = now() 

# LET SOME TIME PASS
Sleep(2000) 

# CALCULATE DIFFERENCE BETWEEN LAST TIME AND CURRENT TIME
$delta = TotalSeconds(now()-CDateTime($tmp)) 

# DISPLAY DIFFERENCE TO USER
print($delta) 

Explain what you'd like to do and I'll help provide direction on the code.

*Edit: I re-read what you typed and maybe you're looking for no decimal level precision? Computers will provide absolute highest precision based on variable type used. If you wish to lower precision to whole numbers (integer), then do this... (note: rounding will occur)


# GET THE CURRENT TIME
$tmp = now() 

# CALCULATE DIFFERENCE BETWEEN LAST TIME AND CURRENT TIME and convert to integer
$delta = CInt(TotalSeconds(now()-CDateTime($tmp)))

# DISPLAY DIFFERENCE TO USER
print($delta) 

Belgium
#6  

Hey DJ,

With all due respect, I think you're wrong here. The time you get from my test code has (almost) nothing to do with the execution time. It will print values up to nearly 1 second, but the entire script executes from start to finish in 00:00:00.0080011. Likewise your code with the 2 second sleep will return anything between 2 and 3 seconds, but will execute in 2 seconds plus only a few milliseconds. The sleep command has some inherent variability (which is why I didnt use it in my test code), but it will be in the micro second range, at worst maybe a millisecond or so, but not 100s of milliseconds.

As an example, running your code:

Start 2: $tmp = now() 5: Sleep(2000) 8: $delta = TotalSeconds(now()-CDateTime($tmp)) 11: print($delta) > 2.9029819 Done (00:00:02.0145256)

As you see, on that run it reported a delta of over 2.9s but executed in 2.01s. If you think about it, even if the sleep command where widely inaccurate and the computer would be super slow, then its still not possible that it measures a time difference that is longer than the time it took to execute the script.

What I am trying to do is rewriting the servo tracking functionality, controlling both eyes and neck, and "intelligently" looking for a face when it loses track. I need to know how long it has been since the last loop, and I need that to be more accurate than 1 second.

BTW, I wonder if this "time rounding" may also be causing the "talk servo" plugin to be widely variable in its accuracy. Sometimes it seems good, sometimes it lags by like a full second, even when saying the same words. Could this be caused by the same underlying problem?

Belgium
#7  

To further prove my point, this little script reports $delta every ~100ms:


repeat ( $i, 1, 100, 1 )
  # GET THE CURRENT TIME
  $tmp = now()
  # CALCULATE DIFFERENCE BETWEEN LAST TIME AND CURRENT TIME
  $delta = TotalSeconds(now()-CDateTime($tmp))
  # DISPLAY DIFFERENCE TO USER
  print($delta)
  sleep(100)
endrepeat

Given that the sleep() command is outside the time measurement code, you would expect $delta to be very close to zero, basically just the time it takes to execute the time conversion and comparison lines. And since every loop iteration is identical, you would expect $delta to be almost identical on each iteration, with at most some slight random variation in execution time.

Thats not what you get.

The output shows $delta increasing with each iteration of the loop by ~0.1s ( exactly 100ms sleep+ the small time it takes to execute the time comparison code, which is what delta SHOULD be measuring), until it approaches 1, then it rolls over again to ~0:

User-inserted image

This is not random or coincidence, the increase of $delta at a rate of 0.1s per iteration, or 0.1s per 0.1s, correlates exactly with the progression of the system clock,not with the time it takes to actually execute the code. So $delta is essentially reporting how far the system clock is from the next full second, which is not what I want it to do:)

edit: im probably making this more complicated than need be. It seems in ezscript now() returns a string representing date/time with integer seconds:

1: $tmp = now() 2: print($tmp) > 11/27/2018 12:57:55 PM

So any measurement can only be accurate to a second? If so, can we have a millis() function? I dont need nanosecond accuracy, but a full second is just too much for many things.

Moreover, CDateTime, at least in C++, appears to only accept integers for seconds.

That said, I still dont understand why my code doesnt return zero or one for $delta though, if indeed now() always returns an integer number of seconds .

So i tried this:


  $tmp = now()
  sleep(500)
  $tmp2 = now()
  $delta = TotalSeconds(CDateTime($tmp2)-CDateTime($tmp))
  print($delta)

That will give exactly zero or exactly one second, so either storing now() in a variable reduces it to an integer-second precision, or CDateTime does, or both.

But given my previous results, Im guessing the now() function within Totalseconds() somehow does get processed with decimals?

Last test:


repeat ( $i, 1, 100, 1 )
  $tmp = now()
  sleep(500)
  $delta = TotalSeconds(now()-CDateTime($tmp))
  print("using now:" + $delta)
  $tmp2 = now()    
  $delta2 = TotalSeconds(CDateTime($tmp2)-CDateTime($tmp))
  print("using CDateTime:" +$delta2)
 
  sleep(1000)
endrepeat

Gives me this:

User-inserted image

So it would appear CDateTime() is causing rounding of seconds, resulting in $delta2 being either zero or one, depending on the clock switching seconds before or after the sleep. Storing the result of now() in a variable may cause rounding too, Im not 100% sure there. But passing now() to TotalSeconds() appears not to cause any rounding, as $delta shows a non integer difference between now() and the rounded/integer $tmp confused

PRO
Synthiam
#8  

Now you’re getting into conversation of the operating system. Microsoft Windows is not a real-time OS and the datetime function precision is not in millisecond accuracy. It’s barely in second accuracy, as you can see from the results of your tests.

Google how the datetime works with windows and the accuracy of system ticks. It’s beyond the control of ARC.

If you can explain what you’re goal is, maybe I can help. A time span can provide more accuracy - but I’ll need to know what you’re goal is.

Belgium
#9  

Dj, this isnt a fundamental limitation of windows; while windows is not a RTOS, surely you can use timers that are more accurate than a full second? I dont need millisecond level assured execution, but I do need to know how much time has elapsed (partially because that time is variable because its not a rtos) and more precisely than a second.

Some googling suggests that In .Net you can use timespan to get 100 nanosecond resolution timers:

https://docs.microsoft.com/en-us/dotnet/api/system.timespan?view=netframework-4.7.2

Its not like I need nano second level of resolution, but a thousand milliseconds truly is an eternity and makes it impossible for me to achieve my goals... Im sure youd agree that sleep() uses milliseconds and not seconds for good reason?

Belgium
#10  

As for what I want to do; in a nutshell I want to achieve smooth and "smart" motion tracking, combining eye and neck servos. I am working on some routines that calculate an average absolute position of the tracked face, and estimate its position when the track is lost, and others that steer the head and eyes towards the target or (gu)estimated target. To make the motion smooth and to guess the position when the track is lost, I need to calculate speeds which is distance/time which really doesnt work when time can be off by as much as a full second

PRO
Synthiam
#11  

You can't use a datetime for that - as you discovered using google. As i previously mentioned, a timespan is the only way. A datetime does not provide the accuracy that fits your requirement. Consider creating a plugin that extends the capability of EZ-Script by adding a timespan command. Timespan is not a datetime.

You will probably get the accuracy you're looking for by using a timespan. A simple class that .Net provides for accurate timespan is System.Diagnostic.Stopwatch. It's good for calculating times between events - which sounds like what you're wanting to do.

Keep in mind that your calculation will not know the physical position of the servo because the real-world and software are different beasts. Instructing a servo to move into a position will take an unknown amount of time. I'm sure you thought of that:) Have fun!

Belgium
#12  

Thats why Im "stepping" the servos one position at a time, that way I also gain control over their speed.

Looks like Ill need to learn VS and plugins after all... Oh well, its a DIY kit right ?;)

PRO
Synthiam
#13  

Here's a plugin example with source code that will get you started: https://synthiam.com/redirect/legacy?table=plugin&id=162

I'd create functions that are something like...

StartTimer( ID ) Reset and start a high resolution timer with the respective ID (identifier). The identifier allows you to have multiple timers running

EndTimer( ID ) Stops and returns the timespan since StartTimer()

GetTimer( ID ) Returns the timespan since StartTimer()

PRO
USA
#14  

@Vertigo: I created a simple plugin "Miscellaneous Utility Plugin" with 3 custom functions: Millis, Ticks, TicksToMilliseconds

Sample ez-script:


#V2 
print("IsHighResolution = " + IsHighResolution())

# a long integer representing the tick counter value of the underlying timer mechanism ***EDITED***.
$start_ticks=ticks()

#The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT), not counting leap seconds
$start_ms=millis()

$start_dt=now()

REPEAT ($ix, 1, 7, 1 )
  sleep(123)

  $delta_ticks=ticks()-$start_ticks
  $delta_ms=millis()-$start_ms
  $delta_dt=TotalSeconds(now()-CDateTime($start_dt))

  print("Ix               :" + $ix)
  print("dt ticks         :" + $delta_ticks)
  print("dt ms(from ticks):" + TicksToMilliseconds($delta_ticks))
  print("dt ms            :" + $delta_ms)
  print("dt seconds(ez)   :" + $delta_dt)
  print("------------------")

ENDREPEAT

Result:

User-inserted image

*** EDITED ***

source code: https://github.com/ppedro74/ezrobot-playground/tree/master/plugins/MiscUtilityPlugin

PRO
Synthiam
#15  

Are you extracting the ticks from datetime object? I don't think the datetime.now.ticks has enough precision for the OP's needs. The datetime extracted from system clock is not meant for high precision. That's the purpose of System.Diagnostic.Stopwatch

Correct me if i'm incorrect

PS, awesome quick response time on getting that plugin online! Impressive!

PRO
USA
#16  

Quote:

Correct me if i'm incorrect
You are correct.

For future reference:

Quote:

A good clock should be both precise and accurate; those are different. As the old joke goes, a stopped clock is exactly accurate twice a day, a clock a minute slow is never accurate at any time. But the clock a minute slow is always precise to the nearest minute, whereas a stopped clock has no useful precision at all.

Why should the DateTime be precise to, say a microsecond when it cannot possibly be accurate to the microsecond? Most people do not have any source for official time signals that are accurate to the microsecond. Therefore giving six digits after the decimal place of precision, the last five of which are garbage would be lying.

Remember, the purpose of DateTime is to represent a date and time. High-precision timings is not at all the purpose of DateTime; as you note, that's the purpose of StopWatch. The purpose of DateTime is to represent a date and time for purposes like displaying the current time to the user, computing the number of days until next Tuesday, and so on.

Source: Eric Lippert https://stackoverflow.com/questions/2143140/c-sharp-datetime-now-precision

Only later i noticed the OP wanted to track time (accuracy). I assumed he wanted a flat time representation for calculations.

I'll fix the plugin Ticks() will get the accuracy needed and Millis() a flat time representation.

you can calculate a delta between two Millis() values but is not accurate.

PRO
USA
#17  

Fixed! Source code updated.

Belgium
#19  

BTW, either would have worked I think. The stopwatch will deviate from the clock on the order of a few seconds per week I read somewhere, which is absolutely no problem, and datetime ticks would be updated every ~15ms, which is also fast enough.

PRO
Synthiam
#20  

Vertigo - no, that's the whole point to the conversation is that you cannot use the system clock (datetime) for accuracy. As you even stated earlier, a TimeSpan is the only way to receive that accuracy

They're different beasts:D

Belgium
#21  

datetime ticks are not accurate enough for performance counters or other special tasks, say stepper motors, but if they are indeed updated every ~15 milliseconds, then that is good for "60FPS smoothness" and 66x better than what we currently have in ezscript due to the integer rounding to the nearest second. It should be accurate enough for my use as I could probably live with 50 to even 100ms. There is a limit to what you can do with servos anyway.

I will test to see if that actually works, but ironically the tests I did already showed that it probably is: if you deduct the x times 100ms rounding-up-to-integer error in each iteration of my 100ms loop test, you get believable results for measuring the execution time that vary no more than a few dozen ms, if that.

Belgium
#22  

Did a quick test to try to find out how often the datetime object is updated - call it resolution instead of accuracy as absolute accuracy isnt important here, I am not trying to calibrate an atomic clock.

Its apparently not 15 millisecond like I read somewhere, its significantly better than that:



$prev = Millis()
$start_ms = Millis()
$resolution=50 # 5 milliseconds

REPEATWHILE(true )

  REPEAT ( $i, 0, 100, 1 )
    sleep($resolution/10)
    $elapsed=  Millis() -$start_ms
    IF ($elapsed==$prev)      
      print("collision, timer resolution limit reached at " +	$resolution/10 + " milliseconds")
      goto(end)
    ENDIF
    $prev =$elapsed
  ENDREPEAT
  print("No collissions at "+	$resolution/10 + " milliseconds")
  $resolution--

ENDREPEATWHILE

:end

Result:

User-inserted image

Indeed, who knew, it turns out you can use floats in sleep() ! I expected to see collisions at >10ms, but when 1ms still didnt trigger a collision, I just tried using fractional numbers expecting an error, but much to my surprise it actually works:). When you reduce the sleep time to below 0.6, then the loop is executed fast enough that millis may return the same time on 2 consecutive iterations and you get a print.

So millis() is indeed updated at least every ms, and the underlying datetime object possibly every 15 microseconds and someone, maybe even me, just confused µs with ms.

Whatever the underlying resolution, 1ms is WAY better than what I need, literally 1000x better than what we had, and there is no point even messing with ticks, unless I want to measure how long it takes to execute some piece of code.

Thanks again PTP!

Belgium
#23  

@DJ

Consider including PTPs plugin in ezb (if he allows it), I cant be the only one needing timers that are more accurate than counting out loud. And even if you dont need the sub second resolution, it is much more convenient to use to determine elapsed time compared to the existing date/time string conversions and custom functions- certainly if you dont need to compare it to the time of day or a date.

Secondly, I would still modify TotalSeconds. The problem is not datetime accuracy, its now() casting strings and the conversion to integer seconds. At the very least make TotalSeconds return an integer rather than float so people dont get confused as I did in to thinking it returns sub second results, when in reality it returns whole seconds plus a random decimal fraction.

Lastly, I mentioned the talk servo plugin. I stand by that, I would check its code, I really suspect there is a similar integer second time rounding issue lurking in there causing the lipsync to be delayed randomly by up to 1 second.

PRO
USA
#24  

Quote:

Consider including PTPs plugin in ezb (if he allows it)
The code is open and the license is Beerware (https://en.wikipedia.org/wiki/Beerware)

No rocket science code:)

@DJ: Plugin functions are only available for the desktop, adding them to the ARC core will be useful to mobile applications.

PRO
USA
#25  

I added 2 new functions: DateTimeTicks(), DateTimeTicksToMilliseconds()

The difference is more significant for short/frequent periods.

the difference was more significant a few years ago, although the differences are relative e.g. hardware, operating system, application code optimization.


$acc_delta_ticks=0
$acc_delta_dt_ticks=0

REPEAT ($ix, 1, 100, 1 )
  $start_ticks=Ticks()
  $start_dt_ticks=DateTimeTicks()

  sleep(10)

  $delta_ticks=Ticks()-$start_ticks
  $delta_dt_ticks=DateTimeTicks()-$start_dt_ticks

  $acc_delta_ticks = $acc_delta_ticks + $delta_ticks
  $acc_delta_dt_ticks = $acc_delta_dt_ticks + $delta_dt_ticks
ENDREPEAT

$ticks_ms = TicksToMilliseconds($acc_delta_ticks)
$dt_ticks_ms = DateTimeTicksToMilliseconds($acc_delta_dt_ticks)
$diff = abs($ticks_ms - $dt_ticks_ms)

print("ticks_ms    = " + $ticks_ms)
print("dt_ticks_ms = " + $dt_ticks_ms)
print("diff        = " + $diff)

User-inserted image

Belgium
#26  

if I read your code correctly, it amplifies the difference a 100 fold, so the actual/average discrepancy is on the order of 0.5ms and much if not all of that could be caused by the execution of the code itself. If the code executed instantly (aside from a perfectly timed sleep), shouldnt you be seeing ~1000ms rather then the ~1380-1440ms you get?

FWIW, this is what I get:

User-inserted image

PRO
USA
#27  

@vertigo: The values are relative.

I can't get less than 50 ms difference, worst case 70 ms.

The script execution: 100 times x 10 ms + script interpretation, overhead etc etc takes between 1300 - 1800 ms.

Belgium
#28  

Well thats my point; looping through the code that is doing the measuring takes almost as long to execute as the time intervals you are trying to measure. So its difficult to conclude where the difference comes from.

Either way, I think we can agree its not an atomic clock, but I also find it difficult to come up with EZB scenarios where a sub millisecond errors per measurement is a big problem.