summaryrefslogtreecommitdiff
path: root/mbghrtime/mbghrtime.c
blob: 46cb7a4e1466f60b3dac85de5eb70ea15904fc2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352

/**************************************************************************
 *
 *  $Id: mbghrtime.c 1.18 2021/04/12 21:58:02Z martin REL_M $
 *
 *  Description:
 *    Main file for mbghrtime program which demonstrates how to access
 *    a Meinberg device via IOCTL calls to read high resolution
 *    time stamps, if supported by the device.
 *
 * -----------------------------------------------------------------------
 *  $Log: mbghrtime.c $
 *  Revision 1.18  2021/04/12 21:58:02Z  martin
 *  Updated printing of usage information.
 *  Revision 1.17  2021/03/21 17:59:51  martin
 *  Updated some comments.
 *  Revision 1.16  2021/03/12 11:50:17  martin
 *  Corrected the wording of some comments.
 *  Revision 1.15  2021/01/27 09:44:30  martin
 *  Don't print a time difference after the first access. With just one
 *  time stamp it is not possible to calculate a time difference.
 *  Revision 1.14  2020/11/04 17:16:12  martin
 *  Added option -C to support checking the continuity of the timestamps
 *  and status sequentially read from a device.
 *  Revision 1.13  2018/11/15 12:12:34  martin
 *  Individual MBG_MICRO_VERSION codes are now obsolete.
 *  Revision 1.12  2017/07/05 19:02:13  martin
 *  New way to maintain version information.
 *  Support build on Windows.
 *  Support raw and burst mode.
 *  New options -s, -u, -v.
 *  Parameters -u and -s imply -c.
 *  Use more functions from common library modules.
 *  Use codes and inline functions from mbgerror.h.
 *  Proper return codes and exit codes.
 *  Revision 1.11  2010/05/21 12:54:33  martin
 *  Print warning if no cycles supported on the target platform
 *  and thus latencies can not be computed.
 *  Revision 1.10  2009/09/29 15:02:15  martin
 *  Updated version number to 3.4.0.
 *  Revision 1.9  2009/07/24 09:50:08  martin
 *  Updated version number to 3.3.0.
 *  Revision 1.8  2009/06/19 14:03:53  martin
 *  Use common mbg_print_hr_timestamp() for unified output format.
 *  Updated version number to 3.2.0.
 *  Revision 1.7  2009/03/20 11:45:16  martin
 *  Updated version number to 3.1.0.
 *  Updated copyright year to include 2009.
 *  Call mbg_get_hr_time_comp() instead of mbg_get_hr_time_cycles().
 *  Revision 1.6  2008/12/22 12:02:00  martin
 *  Updated description, copyright, revision number and string.
 *  Use unified functions from toolutil module.
 *  Accept device name(s) on the command line.
 *  Revision 1.5  2007/07/24 09:32:41  martin
 *  Updated copyright to include 2007.
 *  Revision 1.4  2004/11/08 15:45:22  martin
 *  Modifications to support 64 bit systems in a clean way.
 *  Revision 1.3  2003/04/25 10:28:05  martin
 *  Use new functions from mbgdevio library.
 *  New program version v2.1.
 *  Revision 1.2  2001/11/30 10:01:49  martin
 *  Account for the modified definition of PCPS_HR_TIME which
 *  uses the new structure PCPS_TIME_STAMP now.
 *  Revision 1.1  2001/09/17 15:08:31  martin
 *
 **************************************************************************/

// Include Meinberg headers.
#include <mbgdevio.h>
#include <toolutil.h>       // Common utility functions.
#include <cmp_time_util.h>

// Include system headers.
#include <time.h>
#include <stdio.h>
#include <stdlib.h>



#define MBG_FIRST_COPYRIGHT_YEAR   2001
#define MBG_LAST_COPYRIGHT_YEAR    0     // Use default.

static const char *pname = "mbghrtime";


#define MAX_TS_BURST  1000

static int loops;
static int burst_read;
static int read_raw;
static int must_check_continuity;
static double max_allowed_delta;
static long sleep_secs;
static long sleep_usecs;
static int verbose;



static /*HDR*/
/**
 * @brief Read timestamps and print them immediately.
 *
 * Timestamps are read and then displayed in a single loop.
 * The device is not accessed as fast as possible because
 * printing the timestamp before the next access takes some
 * time to execute.
 * However, this routine can run continuously forever.
 *
 * @param[in]  dh  Valid ::MBG_DEV_HANDLE handle to a Meinberg device.
 *
 * @return ::MBG_SUCCESS on success, else one of the @ref MBG_ERROR_CODES.
 *
 * @see ::show_hr_timestamp_burst
 */
int show_hr_timestamp( MBG_DEV_HANDLE dh )
{
  PCPS_HR_TIME ht = { { 0 } };
  PCPS_HR_TIME prv_ht = { { 0 } };
  int prv_ht_avail = 0;
  int32_t hns_latency = 0;
  int this_loops = loops;
  int rc = MBG_SUCCESS;

  for (;;)
  {
    rc = read_raw ?
           mbg_get_hr_time( dh, &ht ) :
           mbg_get_hr_time_comp( dh, &ht, &hns_latency );

    if ( mbg_cond_err_msg( rc, "mbg_get_hr_time_..." ) )
      goto out;

    mbg_print_hr_time( &ht, hns_latency, prv_ht_avail ? &prv_ht.tstamp : NULL, read_raw, verbose, verbose );

    if ( prv_ht_avail && must_check_continuity )
      mbg_check_continuity( &ht.tstamp, &prv_ht.tstamp, &ht.status, &prv_ht.status, max_allowed_delta );

    prv_ht = ht;
    prv_ht_avail = 1;

    if ( this_loops > 0 )
      this_loops--;

    if ( this_loops == 0 )
      break;

    // If this_loops is < 0, loop forever.

    if ( sleep_secs )
      sleep( sleep_secs );
    else
      if ( sleep_usecs )
        usleep( sleep_usecs );
  }

out:
  return rc;

}  // show_hr_timestamp



static /*HDR*/
/**
 * @brief Read a number of timestamps in a fast loop.
 *
 * A number of timestamps are read to a buffer in a
 * very fast loop. After all timestamps have been read,
 * a second loop prints the timestamps from the buffer.
 *
 * This routine reads the timestamps as fast as possible.
 * However, this can not run continuously forever because
 * the buffer size is somewhat limited.
 *
 * @param[in]  dh  Valid ::MBG_DEV_HANDLE handle to a Meinberg device.
 *
 * @return ::MBG_SUCCESS on success, else one of the @ref MBG_ERROR_CODES.
 *
 * @see ::show_hr_timestamp
 */
int show_hr_timestamp_burst( MBG_DEV_HANDLE dh )
{
  PCPS_HR_TIME ht[MAX_TS_BURST] = { { { 0 } } };
  int32_t hns_latency[MAX_TS_BURST];
  int this_loops = ( loops && ( loops < MAX_TS_BURST ) ) ? loops : MAX_TS_BURST;
  int i;
  int rc = MBG_SUCCESS;

  if ( read_raw )
  {
    for ( i = 0; i < this_loops; i++ )
    {
      rc = mbg_get_hr_time( dh, &ht[i] );

      if ( mbg_cond_err_msg( rc, "mbg_get_hr_time" ) )
        goto out;
    }
  }
  else
    for ( i = 0; i < this_loops; i++ )
    {
      rc = mbg_get_hr_time_comp( dh, &ht[i], &hns_latency[i] );

      if ( mbg_cond_err_msg( rc, "mbg_get_hr_time_comp" ) )
        goto out;
    }

  for ( i = 0; i < this_loops; i++ )
  {
    PCPS_HR_TIME *p_prv_ht = i ? &ht[i - 1] : NULL;
    mbg_print_hr_time( &ht[i], hns_latency[i], &p_prv_ht->tstamp, read_raw, verbose, verbose );
  }

out:
  return rc;

}  // show_hr_timestamp_burst



static /*HDR*/
int do_mbghrtime( MBG_DEV_HANDLE dh, const PCPS_DEV *p_dev )
{
  int rc = mbg_chk_dev_has_hr_time( dh );

  if ( mbg_rc_is_error( rc ) )
  {
    if ( rc == MBG_ERR_NOT_SUPP_BY_DEV )  // ### TODO not_supp
      printf( "High resolution time not supported by this device.\n" );
    else
      mbg_cond_err_msg( rc, "mbg_chk_dev_has_hr_time" );

    goto done;
  }

  if ( burst_read )
    show_hr_timestamp_burst( dh );
  else
    show_hr_timestamp( dh );

done:
  return rc;

}  // do_mbghrtime

static MBG_DEV_HANDLER_FNC do_mbghrtime;



static /*HDR*/
void usage( void )
{
  mbg_print_usage_intro( pname,
    "This example program reads high resolution time stamps (HR time)\n"
    "from a device.\n"
    "This works only for devices which support high resolution time (HR time)."
  );
  mbg_print_help_options();
  mbg_print_opt_info( "-c", "Run continuously" );
  mbg_print_opt_info( "-n num", "Run num loops" );
  mbg_print_opt_info( "-b", "Burst read first, then show results" );
  mbg_print_opt_info( "-r", "Read raw time stamps, no cycles" );
  mbg_print_opt_info( "-C val", "Check for continuity, max allowed delta (seconds, with fractions)" );
  mbg_print_opt_info( "-s num", "Sleep num seconds between calls (implies -c)" );
  mbg_print_opt_info( "-u num", "Sleep num microseconds between calls (implies -c)" );
  mbg_print_opt_info( "-v", "Increase verbosity" );
  mbg_print_device_options( DEV_OPT_PRINT_BUS_LEVEL );
  puts( "" );

}  // usage



int main( int argc, char *argv[] )
{
  int rc;
  int c;

  mbg_print_program_info( pname, MBG_FIRST_COPYRIGHT_YEAR, MBG_LAST_COPYRIGHT_YEAR );

  // Check command line parameters.
  while ( ( c = getopt( argc, argv, "bcn:rC:s:u:vh?" ) ) != -1 )
  {
    switch ( c )
    {
      case 'b':
        burst_read = 1;
        break;

      case 'c':
        loops = -1;
        break;

      case 'n':
        loops = atoi( optarg );
        break;

      case 'r':
        read_raw = 1;
        break;

      case 'C':
       must_check_continuity = 1;
       max_allowed_delta = atof( optarg ) * 1E6;
       break;

      case 's':
        sleep_secs = atoi( optarg );
        loops = -1;
        break;

      case 'u':
        sleep_usecs = atoi( optarg );
        loops = -1;
        break;

      case 'v':
        verbose++;
        break;

      case 'h':
      case '?':
      default:
        must_print_usage = true;
    }
  }

  if ( must_print_usage )
  {
    usage();
    return MBG_EXIT_CODE_USAGE;
  }


  #if !MBG_PC_CYCLES_SUPPORTED
    printf( "** Warning: No cycles support to compute real latencies on this platform!\n" );

    if ( !read_raw )
    {
      read_raw = 1;
      printf( "** Falling back to raw mode.\n" );
    }

    printf( "\n" );
  #endif


  // Handle each of the specified devices.
  rc = mbg_handle_devices( argc, argv, optind, do_mbghrtime, 0 );

  return mbg_rc_is_success( rc ) ? MBG_EXIT_CODE_SUCCESS : MBG_EXIT_CODE_FAIL;
}