Hi folks,
I'm Jim and I'm new with LabJack and with Lua languaje.
I'm using a T7-Pro ADQ and I'm triying to log 9 analog channels as fast as a T7 can, into uSD (each readed line with it's own timestamp).
I've configured clock0 timer (for 0.1 microsecond resolution) and It's working fine, but RTC epoch timer (61500) it's not responding during my test (or I'm reading it in a bad way).
So here's a piece of my code (just I've modified on of the starting examples), and I've attached my full code.
local timeCounterStart1 = mbRead(44908, 1) --reading start time from clock0
local timeCounterEnd1 = 0
while true do
local localTimeEpoch = mbRead(61500, 1) --reading RTC epoch core time
while true do
if checkInterval(0) then --interval completed
voltage0= mbRead(0, 3) --voltage on AIN0, address is 0, type is 3
voltage1= mbRead(2, 3) --voltage on AIN1, address is 2, type is 3
voltage2= mbRead(4, 3) --voltage on AIN2, address is 4, type is 3
voltage3= mbRead(6, 3) --voltage on AIN3, address is 6, type is 3
voltage4= mbRead(8, 3) --voltage on AIN4, address is 8, type is 3
voltage5= mbRead(10, 3) --voltage on AIN5, address is 10, type is 3
voltage6= mbRead(12, 3) --voltage on AIN6, address is 12, type is 3
voltage7= mbRead(14, 3) --voltage on AIN7, address is 14, type is 3
voltage8= mbRead(16, 3) --voltage on AIN8, address is 16, type is 3
timeCounterEnd1 = mbRead(44908, 1) --reading clock0 again
local elapsedTime1 = 0
if timeCounterEnd1 < timeCounterStart1 then --lets calculate single AIN read elapsed time
elapsedTime1 = 10000000 - timeCounterStart1 + timeCounterEnd1 --for timer count overflow
else
elapsedTime1 = timeCounterEnd1 - timeCounterStart1
end
timeCounterStart1 = timeCounterEnd1 --save last time for next run
dateStr = string.format("%u, %u", localTimeEpoch, elapsedTime1)
voltageStr = string.format("%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f", voltage0, voltage1, voltage2, voltage3, voltage0, voltage4, voltage5, voltage6, voltage7, voltage8)
dataBunch = dataBunch .. dateStr .. delimiter .. voltageStr .. "\n" --save data so we can write slower to file
count0= count0 + 1
end
if count0 >= bunchLimit then
break
end
end
file:write(dataBunch)
dataBunch = ""
count0 = 0;
count = count + 1
if count >= iLimit then
break
end
end
I think that timer procesor it's overloaded or I'm doing something really wrong.
I'll appreciate any advice.
Tanks in advance.
Jim
The lua script is working with IEEE 754 32-bit floats. That data type can only handle 23 bits of information (the rest is sign and exponent). In order to work with larger numbers they need to be read as two 16-bit numbers via MB.RA. Have a look at some of the logging examples for some copy-paste timestamp generation code.
Hey!
I just tried what you said and it worked like a charm :)
Thank you very much.
Here it's what I've modified into my code, so it can help anyone with same issue/doubt:
localTimeEpoch, error = mbReadArray(61500, 1, 2) --reading RTC epoch core time
Also I've attached full code.
Regards
Dear all,
I tried what Jimmy suggested, but I am still getting the wrong Epoch. In fact, if I compare the time I get from T7pro with the official UNIX time, the first 10 digits (localTimeEpoch[1]) should be UNIX time in seconds and they should be updated every second, but it is not (see picture issue_time_T9.png). Notably in issue_time_T9.png, I compare the time device time (address 61500) with the official one.
Furthermore, by running the code two times (the second run started 5 seconds after the first one), the second run showed an Epoch smaller than the first one (see issue_time_incorrect.png).
Could someone give me a detailed explanation about this issue? Unfortunately, I cannot figure it out from the documentation.
Thank you in advance
Jimmy Toro, we should have caught this earlier, but the code snippet in your post is not a valid way to read that register. The Lua system on the T7 stores all values as single precision floats so to get the time stamp (seconds from epoch) you need to read the value as two 16-bit integers and print them as such:
time, error = MB.RA(61500, 0, 2)
print("time (s) from epoch:", time[1], time[2], error)
The code snippet you wrote and how JV used it provides a truncated 32bit UINT value, an erroneous value, and then the error code:
invalidTime, error = MB.RA(61500, 1, 2)
print("truncated uint32 as float32:", invalidTime[1], "invalid value:", invalidTime[2], "error code:", error)
JV, combining these values in a Lua Script to set a single variable as "num ms since epoch" is not possible. You will need to save both of these values and combine them in a program that can handle UINT64 values or double precision floats. The closest that you can easily get to combining the values into a single value is to convert the values to hex and then into a string. The Hex string can get interpreted as a decimal number (Hex to Decimal converter), and then when you go to convert the value to a millisecond time (in order to convert to UTC time & date) you need to add in 3x "0"s.
time, error = MB.RA(61500, 0, 2)
timeTable = MB.RA(61510, 0, 6)
print("Time (2x 16-bit values in decimal form):", string.format("%d, %d",time[1], time[2]), "Error Code:",error)
print("Time (2x 16-bit values in hex form):", string.format("0x%x, 0x%x", time[1], time[2]))
print("Time (hex):", string.format("0x%x%x", time[1], time[2]))
print("Time (str):", string.format("%04d-%02d-%02d %02d-%02d-%02d", timeTable[1], timeTable[2], timeTable[3], timeTable[4], timeTable[5], timeTable[6]))
A snippet of code that prints all of this out every second is:
LJ.IntervalConfig(0, 1000) --set interval to 1000 for 1000ms
while true do
if LJ.CheckInterval(0) then --interval completed
time, error = MB.RA(61500, 0, 2)
timeTable = MB.RA(61510, 0, 6)
print("Time (2x 16-bit values in decimal form):", string.format("%d, %d",time[1], time[2]), "Error Code:",error)
print("Time (2x 16-bit values in hex form):", string.format("0x%x, 0x%x", time[1], time[2]))
print("Time (hex):", string.format("0x%x%x", time[1], time[2]))
print("Time (str):", string.format("%04d-%02d-%02d %02d-%02d-%02d", timeTable[1], timeTable[2], timeTable[3], timeTable[4], timeTable[5], timeTable[6]))
print("")
end
end
JV, it does look like your device's time is slightly off from the time reported by currentmillis.com, what does your device report as its time at the bottom of the Device Info tab? In Kipling 3.1.14 there is a "Current Time" section under the "Hardware Installed" & "Enabled Features" sections. You can also set your device's RTC time here so that it is the same as your computer's time. You can also configure the device to automatically update its internal time using SNTP time (requires an ethernet connection), you can do this through the register matrix tab and adding the registers mentioned in the RTC section of the T-Series datasheet.
Dear LJ support team,
thank you very much for the quick answer. Saving the Epoch in hex format in a file for post-processing is absolutely fine. As I need the timestamp in ms, I save the Epoch in ms (hex) at the beginning of the file and register the ms elapsed using T7's core clock.
mbRead = MB.R
-- Read local date and time (UTC) from device's clock
local timeTable, error = MB.RA(61510, 0, 6)
local dateStr = string.format("%04d-%02d-%02d %02d-%02d-%02d", timeTable[1],
timeTable[2], timeTable[3], timeTable[4], timeTable[5], timeTable[6])
-- File name
local fileExtension = ".csv"
local fileName = dateStr..fileExtension
-- Create (open) a new file in the uSD card
local file = io.open(fileName, "w")
-- Check if the file has been created and openend properly
if file then
print("Opened File on uSD Card", fileName)
else
print("!! Failed to open file on uSD Card !!", fileName)
MB.W(6000, 1, 0)
end
-- UTC time written in 2xUINT16
time, error = MB.RA(61500, 0, 2)
-- read CORE_TIMER at 40MHz for registering milliseconds
Tstart = mbRead(61520,2)
local UTC_hex = string.format("0x%x%x", time[1], time[2])
-- reference time
file:write(string.format("UNIX: "..UTC_hex.."\n"))
file:write(string.format("ELAPSED TIME [ms],VOLT\n"))
for i=1,10000 do
local Tend = mbRead(61520,2) -- read CORE_TIMER at 40MHz
local Telapsed = ((Tend - Tstart) / 40000000.0)
file:write(string.format("%.4f,%.2f\n",Telapsed,i))
end
-- Close the file
file:close()
print("File closed")
MB.W(6000,1,0)
I hope this script is correct and can help many LJ's users.
Regarding the device time, there was an offset between device and computer times. I noticed that the device is few seconds ahead of my computer (see picture), therefore I must update the time daily.
Best regards
JV, thanks for posting. Referring specifically to:
Tstart = mbRead(61520,2)
and
for i=1,10000 do
local Tend = mbRead(61520,2) -- read CORE_TIMER at 40MHz
local Telapsed = ((Tend - Tstart) / 40000000.0)
file:write(string.format("%.4f,%.2f\n",Telapsed,i))
end
This methidology you are using will give you more data to later go back and determine how long your script took to run however keep in mind that the CORE_TIMER value rolls-over back to zero roughly every 100 seconds. Also note that it is also a UINT32 value so the higher CORE_TIMER values that you are reading are going to be truncated due to Lua's only data type being a 32-bit Float.
Yes, correct. Inside the for loop, it is necessary to check Telapsed. When 100s of Telapsed are reached, then I must read and save a new epoch, and also update Tstart. Truncation won't be a problem for me as in the worst case the worst case I would lose precision in terms of ns
thanks a lot for your help
Thanks for your advices.
I did notice what support says, but I had a problem posting the correct answer here (I don't remmember exactly, but post never work), so sorry about that.
What I'm doing right now is:
localTimeEpoch, error = mbReadArray(61500, 0, 4) --reading RTC epoch timer
dateStr=string.format("%u %u %u %u,",localTimeEpoch[1],localTimeEpoch[2],localTimeEpoch[3],localTimeEpoch[4])
Where
Any advice or correction will be appreciate.
Jimmy,
I'm not sure if you have been intentionally doing this, but your observations about localTimeEpoch[3] and [4] are correct. In your function call mbReadArray(61500, 0, 4) you have been reading the "RTC_TIME_S" and the "SYSTEM_COUNTER_10KHZ" registers. The register wasn't documented on the RTC datasheet page (it is now added). The "SYSTEM_COUNTER_10KHZ" register provides you the ms portion of the RTC_TIME_S (time in seconds) value. You can read this value directly using the MB.R(61502, 1) and you shouldn't experience any truncation issues as the values range is 0-9999.
Unfortunately, your statement about being able to bit-shift data and building a correct num-seconds value is still incorrect. Here is a more complete example code snippet that reads timer information 2 times per second.
local numSec = {}
numSec[1] = 0 --upper uint16
numSec[2] = 0 --lower uint16
numSec[3] = 0 --will be zero
numSec[4] = 0 --10KHZ counter value.
local mbReadArray=MB.RA
LJ.IntervalConfig(0, 500)
local checkInterval=LJ.CheckInterval
while true do
if checkInterval(0) then
numSec, error = mbReadArray(61500, 0, 4)
print(string.format("%d %d, ms portion: %f", numSec[1], numSec[2], numSec[4]/10000))
print(string.format("0x%x 0x%x", numSec[1], numSec[2]))
print(string.format("Upper shifted8 bits: 0x%x 0x%x", bit.lshift(numSec[1],8), numSec[2]))
print(string.format("Truncated Upper: 0x%x 0x%x", bit.lshift(numSec[1],16), numSec[2]))
print(string.format("Truncated Value: 0x%x", bit.lshift(numSec[1],16)+ numSec[2]))
print(string.format("Correct Value: 0x%x%x", numSec[1], numSec[2]))
print("\n")
end
end
Hi again,
About read 4 registers, I just noticed that the next register was the 10Mhz clock and that I can read any amount of registers using mbReadArray.
For example: Right now I'm reading 9 analog channels at once:
local voltage = {}
voltage[0] = 0
voltage[1] = 0
...
voltage[16] = 0
voltage[17] = 0
voltage[18] = 0
voltage = mbReadArray(0,0,18)
(do I need to declare each voltage[xx] sub-variable?)
About left shift: I'm not doing that into T7-Pro (just what you said, variables has only 16 bits), I'm doing that in post-processing (with a math software).
Other way to do same thing (post-procesing again):
into T7: print(string.format("a: %d b: %d", numSec[1], numSec[2])
Post-procces: a* 65536 +b = timestamp in sec.
Hope I'm right this time (at least, we had good results post-proccessing T7 saved files).
Regards
Glad to hear you are doing the data shifting for all of the time stamps in an external program. The mb.ra function is definitely useful in many different ways.
As for your AIN question, take a look at the "Data Types" section on the Lua Scripting datasheet page. If you want to read 9 analog inputs at the same time do something like this (Hint use data type 3, float32 value):
numAIN = 9
vals = MB.RA(0, 3, numAIN)
for i=1, (numAIN) do
print(string.format("AIN%d: %f", i-1, vals[i]))
end
MB.W(6000, 1, 0)