Timestamps for Embedded System

Timestamps for embedded system

If you have C++11 you can use the <chrono> and <ctime> library like this:

#include <ctime>
#include <string>
#include <chrono>
#include <sstream>
#include <iomanip>
#include <iostream>

// use strftime to format time_t into a "date time"
std::string date_time(std::time_t posix)
{
char buf[20]; // big enough for 2015-07-08 10:06:51\0
std::tm tp = *std::localtime(&posix);
return {buf, std::strftime(buf, sizeof(buf), "%F %T", &tp)};
}

std::string stamp()
{
using namespace std;
using namespace std::chrono;

// get absolute wall time
auto now = system_clock::now();

// find the number of milliseconds
auto ms = duration_cast<milliseconds>(now.time_since_epoch()) % 1000;

// build output string
std::ostringstream oss;
oss.fill('0');

// convert absolute time to time_t seconds
// and convert to "date time"
oss << date_time(system_clock::to_time_t(now));
oss << '.' << setw(3) << ms.count();

return oss.str();
}

int main()
{
std::cout << stamp() << '\n';
}

Output:

2015-07-08 10:13:29.930

Note:

If you want higher resolution you can use microseconds like this:

std::string stamp()
{
using namespace std;
using namespace std::chrono;

auto now = system_clock::now();

// use microseconds % 1000000 now
auto us = duration_cast<microseconds>(now.time_since_epoch()) % 1000000;

std::ostringstream oss;
oss.fill('0');

oss << date_time(system_clock::to_time_t(now));
oss << '.' << setw(6) << us.count();

return oss.str();
}

Output:

2015-07-08 10:20:39.454163

What timestamp would have the best space efficiency with a 10 year span and 1 second resolution?

i need to represent next minimum 10 years with resolution of second

If you use an int32_t you're good until 2038 with these timestamps, and with uint32_t you can cover until 2106. Using localtime, gmtime and such you can convert them into struct tm if you need and extract the day, month etc.

Problem with time() function in embedded application with C

Is there a replacement for this function?

The available source of time is hardware dependent, and the library is hardware independent. As such you have to provide implementations for library functions with hardware dependencies yourself, or they may be included in a vendor provided board-support package (BSP) in some cases. The header time.h provides the standard declaration your implementation must conform to, while the library in this case provides a default implementation that is not fully functional.

If you are using armcc (as used in the Keil ARM MDK for example), then the default time() implementation uses semi-hosting. That is it obtains time from the development host rather then than the target hardware.

Semi-hosting will work only when debugging while connected to a debug host. However in that case time() should return -1 rather than a processor restart. This function is not the cause of the restart - you could demonstrate that by removing it, and the restart will still occur. The restart is simply that you explicitly return from main() - what else is the runtime supposed to do? It will either restart directly or it may enter a busy loop, where a watchdog timer expiry might restart it. It depends on your C run-time environment implementation. Moreover the behaviour may differ depending on whether the debugger is connected or not; it is possible to determine when the on-chip debug is active and to conditionally execute a break-point instruction for example to interrupt the debugger.

To have time() work correctly with your target hardware rather than use semi-hosting, you must re-implement it. It is defined as a weak-link and any implementation you provide will override the default, so somewhere in your project you must have a function:

#include <time.h>

time_t time( time_t* timep )
{
int hour = 0 ;
int minute = 0 ;
int second = 0 ;
int day_of_month = 0 ;
int month = 0 ;
int year = 0 ;

// Your code here to fill time/date from clock source
...

// Normalise to time.h library epoch time_t (normally Unix epoch)
struct tm timeinfo;
timeinfo.tm_mon = month - 1 ; // check assumption here Jan = 0 in tm
timeinfo.tm_mday = day_of_month ;
timeinfo.tm_year = year + 100 ; // check assumption here years start from 1900 in tm
timeinfo.tm_hour = hour ;
timeinfo.tm_min = minute;
timeinfo.tm_sec = second;

// Convert to timestamp
time_t t = mktime(&timeinfo);
if( timep != NULL )
{
*timep = t ;
}

return t;
}

If your time source requires any kind of initialisation before it will work, you can do that in a number of ways, for example:

  • Place the initialisation code in the run-time start-up code that runs before main(). For example your start-up code may have a function called SysInit() or similar where you should do this.
  • Require the developer to perform necessary initialisation before time() is used.
  • Initialise on first use by modifying the time() function as below :

#include <time.h>
#include <stdbool.h>

time_t time( time_t* timep )
{
static bool initialised = false ;
if( !initialised )
{
initialised = true ;

// your clock source initialisation here
...
}

...

This last method is probably the simplest and least error prone and does not saddle the system with code that it might not need if the application does not use time().

Your example code includes stdio.h but does not use it, but note that the default stdio implementation similarly relies on semi-hosting, and may need re-targetting

Python embedded. timestamp() return same time over multiple seconds

The type of a timestamp is float, which is a floating point type of the width of the Python build. A 32-bit float is not sufficient to express the timestamp with second precision:

>>> import struct
>>> def as_f32(num: float): return struct.unpack('f', struct.pack('f', num))[0]
>>> def as_f64(num: float): return struct.unpack('d', struct.pack('d', num))[0]
>>> # 32-bit expressible timestamps in a 10 second range
>>> set(map(as_f32, range(1614270075, 1614270085)))
{1614270080.0}
>>> # 64-bit expressible timestamps in a 10 second range
>>> set(map(as_f64, range(1614270075, 1614270085)))
{1614270075.0, 1614270076.0, 1614270077.0, 1614270078.0, 1614270079.0, 1614270080.0, 1614270081.0, 1614270082.0, 1614270083.0, 1614270084.0}

Use a 64-bit build if the full precision is needed.

How do you measure the time a function takes to execute?

The best way to do that on an embedded system is to set an external hardware pin when you enter the function and clear it when you leave the function. This is done preferably with a little assembly instruction so you don't skew your results too much.

Edit: One of the benefits is that you can do it in your actual application and you don't need any special test code. External debug pins like that are (should be!) standard practice for every embedded system.

How to sign a PDF with embedded timestamp and LTV enabled?

First, based on @mkl comment, I added the TSA server certificate to the Adobe Trusted Certificates so that the message

the signature includes an embedded timestamp but it could not be
verified

became

the signature includes an embedded timestamp

And to solve

Signature is not LTV enabled and will expire after (...)

I could note that using

List<CrlClient> crlList = new ArrayList<>();
crlList.add(new CrlClientOnline(chain));

there were some CRL's (some of the certificates had more than one distribution point) not being added - making the pdf LTV not enabled. To solve it I've done this way:

// long term validation (LTV)
List<CrlClient> crlList = new ArrayList<>();

for(Certificate cert : chain) {
X509Certificate c = (X509Certificate)cert;
List<String> crls = this.getCrlDistributionPoints(c);
if(crls != null && !crls.isEmpty()) {
crlList.add(new CrlClientOnline(crls.toArray(new String[crls.size()])));
}
}

private List<String> getCrlDistributionPoints(final X509Certificate cert) throws Exception {
final byte[] crldpExt = cert.getExtensionValue(X509Extension.cRLDistributionPoints.getId());
if (crldpExt == null) {
final List<String> emptyList = new ArrayList<String>();
return emptyList;
}
ASN1InputStream oAsnInStream = null;
ASN1InputStream oAsnInStream2 = null;
List<String> crlUrls = new ArrayList<String>();

try {
oAsnInStream = new ASN1InputStream(new ByteArrayInputStream(crldpExt));
final ASN1Object derObjCrlDP = oAsnInStream.readObject();
final DEROctetString dosCrlDP = (DEROctetString) derObjCrlDP;
final byte[] crldpExtOctets = dosCrlDP.getOctets();
oAsnInStream2 = new ASN1InputStream(new ByteArrayInputStream(crldpExtOctets));
final ASN1Object derObj2 = oAsnInStream2.readObject();
final CRLDistPoint distPoint = CRLDistPoint.getInstance(derObj2);
for (final DistributionPoint dp : distPoint.getDistributionPoints()) {
final DistributionPointName dpn = dp.getDistributionPoint();
// Look for URIs in fullName
if (dpn != null) {
if (dpn.getType() == DistributionPointName.FULL_NAME) {
final GeneralName[] genNames = GeneralNames.getInstance(dpn.getName()).getNames();
// Look for an URI
for (int j = 0; j < genNames.length; j++) {
if (genNames[j].getTagNo() == GeneralName.uniformResourceIdentifier) {
final String url = DERIA5String.getInstance(genNames[j].getName()).getString();
crlUrls.add(url);
}
}
}
}
}
} catch(IOException e) {
throw new Exception(e.getMessage(), e);
} finally {
IOUtils.closeQuietly(oAsnInStream);
IOUtils.closeQuietly(oAsnInStream2);
}

return crlUrls;
}


Related Topics



Leave a reply



Submit