OMAPI  1.8
Open Movement Public API
verify.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009-2012, Newcastle University, UK.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * 1. Redistributions of source code must retain the above copyright notice,
8  * this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23  * POSSIBILITY OF SUCH DAMAGE.
24  */
25 
41 /* Headers */
42 #ifdef _WIN32
43 #define _CRT_SECURE_NO_WARNINGS
44 #include <windows.h>
45 #include <io.h>
46 #define access _access
47 #else
48 #include <unistd.h>
49 #endif
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <math.h>
54 #include <time.h>
55 #include <stdbool.h>
56 #include <sys/timeb.h>
57 
58 #ifdef _WIN32
59 #define gmtime_r(timer, result) gmtime_s(result, timer)
60 #define timegm _mkgmtime
61 #define tzset _tzset
62 #endif
63 
64 
65 
66 /* API header */
67 #include "omapi.h"
68 
69 
70 /* LEDs */
71 #define LED_PROCESSING OM_LED_YELLOW
72 #define LED_OK OM_LED_MAGENTA
73 #define LED_WARNING OM_LED_MAGENTA
74 #define LED_FAILED OM_LED_BLUE
75 #define LED_ERROR_COMMS OM_LED_CYAN
76 
77 /* Options */
78 #define VERIFY_OPTION_ALL 0x01
79 #define VERIFY_OPTION_NO_CHECK_STOP 0x02
80 #define VERIFY_OPTION_OUTPUT_NEW 0x04
81 
82 /* Error measures */
83 #define STUCK_COUNT (50 * 120)
84 #define AVERAGE_FACTOR 0.00001
85 #define AVERAGE_RANGE_MAX 0.400
86 #define AVERAGE_RANGE_OFF 0.300
87 
88 #define IGNORE_RECENT_RESTARTS (6*60*60) // Enable this if the devices have been connected/disconnected recently (causing a break in the data)
89 
90 
91 
92 #define ID_NAND
93 
94 typedef struct
95 {
97 #ifdef ID_NAND
98  unsigned char nandId[6];
99  int nandType;
100 #endif
101  char *filename;
102  bool hasGyro;
103  // ???!!!
104 } download_t;
105 
106 static int globalOptions = 0;
107 static int globalAllowedRestarts = 0;
108 
109 #ifdef ID_NAND
110 // "NANDID=%02x:%02x:%02x:%02x:%02x:%02x,%d\r\n", id[0], id[1], id[2], id[3], id[4], id[5], nandPresent
111 static const char NAND_DEVICE_DONT_CARE[6] = { 0x00 };
112 static const char NAND_DEVICE_HY27UF084G2x[6] = { 0xAD, 0xDC, 0x00 }; // "NAND_DEVICE_ALT"
113  //?? 0xad,0xdc,0x84,0x25,0xad,0x00,
114 static const char NAND_DEVICE_HY27UF084G2B[6] = { 0xAD, 0xDC, 0x10, 0x95, 0x54, 0x00 }; // 1 "NAND_DEVICE"
115 static const char NAND_DEVICE_HY27UF084G2M[6] = { 0xAD, 0xDC, 0x80, 0x95, 0xAD, 0x00 }; // 2 (one matched by "NAND_DEVICE_ALT", the on-board check terminates after the first two bytes as it matches against NAND_DEVICE_HY27UF084G2x)
116 static const char NAND_DEVICE_MT29F8G08AAA[6] = { 0x2C, 0xD3, 0x90, 0x2E, 0x64, 0x00 }; // 3
117 static const char NAND_DEVICE_S34ML04G1[6] = { 0x01, 0xDC, 0x90, 0x95, 0x54, 0x00 }; // 4 "NAND_DEVICE_ALT2"
118 static const char NAND_DEVICE_MT29F8G08ABADA[6] = { 0x2C, 0xDC, 0x90, 0x95, 0x56, 0x00 }; // 5 "NAND_DEVICE_ALT3" (actually 4G NAND_DEVICE_MT29F4G..., the on-board check terminates after the first two bytes)
119 
120 static int OmGetNandId(int deviceId, unsigned char *id, int *present, int *identified)
121 {
122  int status;
123  char response[256], *parts[16] = {0};
124  unsigned char localId[6] = {0};
125  int tempId[6] = {0};
126  int i;
127 
128  status = OmCommand(deviceId, "\r\nSTATUS 7\r\n", response, 255, "NANDID=", 2000, parts, 15);
129  if (OM_FAILED(status)) return status;
130  // "NANDID=0"
131  if (parts[1] == NULL) { return OM_E_UNEXPECTED_RESPONSE; }
132  if (sscanf(parts[1], "%x:%x:%x:%x:%x:%x", &tempId[0], &tempId[1], &tempId[2], &tempId[3], &tempId[4], &tempId[5]) != 6) { return OM_E_UNEXPECTED_RESPONSE; }
133  for (i = 0; i < 6; i++) { localId[i] = (unsigned char)tempId[i]; }
134  if (id != NULL) { memcpy(id, localId, 6); }
135  if (identified != NULL)
136  {
137  *identified = -2;
138  if (memcmp(NAND_DEVICE_HY27UF084G2B, (char *)localId, 6) == 0) { *identified = 1; }
139  else if (memcmp(NAND_DEVICE_HY27UF084G2M, (char *)localId, 6) == 0) { *identified = 2; }
140  else if (memcmp(NAND_DEVICE_MT29F8G08AAA, (char *)localId, 6) == 0) { *identified = 3; }
141  else if (memcmp(NAND_DEVICE_S34ML04G1, (char *)localId, 6) == 0) { *identified = 4; }
142  else if (memcmp(NAND_DEVICE_MT29F8G08ABADA, (char *)localId, 6) == 0) { *identified = 5; }
143  }
144  if (parts[2] == NULL) { return OM_E_UNEXPECTED_RESPONSE; }
145  if (present != NULL)
146  {
147  *present = atoi(parts[2]);
148  }
149  return OM_OK;
150 }
151 #endif
152 
153 
154 FILE *outfile;
155 
156 // Calculate epoch the time in seconds from a packed time
157 time_t TimeSerial(OM_DATETIME timestamp)
158 {
159  time_t tSec; // Seconds since epoch
160  struct tm tParts = {0}; // Time elements (YMDHMS)
161  tParts.tm_year = OM_DATETIME_YEAR(timestamp) - 1900;
162  tParts.tm_mon = OM_DATETIME_MONTH(timestamp) - 1;
163  tParts.tm_mday = OM_DATETIME_DAY(timestamp);
164  tParts.tm_hour = OM_DATETIME_HOURS(timestamp);
165  tParts.tm_min = OM_DATETIME_MINUTES(timestamp);
166  tParts.tm_sec = OM_DATETIME_SECONDS(timestamp);
167  tSec = timegm(&tParts); // Pack from YMDHMS
168  return tSec;
169 }
170 
171 
172 // Calculate the time in 1/65536th seconds from a packed time
173 unsigned long long Ticks(OM_DATETIME timestamp, unsigned short fractional)
174 {
175  time_t tSec = TimeSerial(timestamp);
176  return ((unsigned long long)tSec << 16) + fractional;
177 }
178 
179 // Returns the number of milliseconds since the epoch
180 unsigned long long now(void)
181 {
182  struct timeb tp;
183  ftime(&tp);
184  return (unsigned long long)tp.time * 1000 + tp.millitm;
185 }
186 
187 // Returns a formatted date/time string for the specific number of milliseconds since the epoch
188 const char *formattedtime(unsigned long long milliseconds)
189 {
190  static char output[] = "YYYY-MM-DD HH:MM:SS.fff";
191  struct tm *today;
192  struct timeb tp = {0};
193  tp.time = (time_t)(milliseconds / 1000);
194  tp.millitm = (unsigned short)(milliseconds % 1000);
195  tzset();
196  today = localtime(&(tp.time));
197  sprintf(output, "%04d-%02d-%02d %02d:%02d:%02d.%03d", 1900 + today->tm_year, today->tm_mon + 1, today->tm_mday, today->tm_hour, today->tm_min, today->tm_sec, tp.millitm);
198  return output;
199 }
200 
201 // Device has gyro?
202 static bool hasGyro(int deviceId)
203 {
204  char serialBuffer[OM_MAX_PATH];
205  if (OM_FAILED(OmGetDeviceSerial(deviceId, serialBuffer))) return false;
206  return memcmp(serialBuffer, "AX6", 3) == 0;
207 }
208 
209 /* Conversion function */
210 int verify_process(int id, const char *infile, download_t *download, int globalOptions)
211 {
212  char output = 0;
213  OmReaderHandle reader;
214  int batteryStartPercent = 0, batteryEndPercent = 0;
215  int batteryMaxPercent = -1, batteryMinPercent = -1;
216  unsigned long long veryFirstTime = 0; // First time in file
217  unsigned long long firstTime = 0, lastTime = 0; // First & last time in current recording block
218  unsigned long long blockStart = 0;
219  unsigned long long previousBlockEnd = 0;
220  unsigned int minLight = 0xffff;
221  unsigned int previousSequenceId = -1;
222  int block;
223  int day = 0;
224  unsigned int totalSamples = 0;
225  unsigned int errorFile = 0, errorEvent = 0, errorStuck = 0, errorRange = 0, errorBreaks = 0, errorRate = 0;
226  double maxAv = 0.0, peakAv = 0.0;
227  char first = 1;
228  unsigned long long minInterval = 0, maxInterval = 0;
229  unsigned long long lastBlockStart = 0;
230  unsigned long long totalDuration = 0;
231  float breakTime = 0.0f;
232  int firstPacket;
233  int lastSecond;
234  int restarts = 0;
236  char outOfRange = 0;
237  char label[256];
238  char line[768];
239  int retval;
240  float percentPerHour = 0;
241  // old statics
242  short lx = 0, ly = 0, lz = 0, stuck = 0;
243  short lgx = 0, lgy = 0, lgz = 0, gstuck = 0;
244  char timeString[25]; /* "YYYY-MM-DD hh:mm:ss.000"; */
245  int seconds, packetCount = 0;
246  double av = 0.0;
247  int lastHour = -1;
248  char description[1024] = "";
249 
250  if (infile == NULL || infile[0] == '\0')
251  {
252  fprintf(stderr, "ERROR: File not specified\n");
253  return -2;
254  }
255 
256  fprintf(stderr, "FILE: %s\n", infile);
257  sprintf(label, infile);
258  if (id >= 0) { sprintf(label, "%d", id); }
259 
260  /* Open the binary file reader on the input file */
261  reader = OmReaderOpen(infile);
262  if (reader == NULL)
263  {
264  fprintf(stderr, "ERROR: Problem opening file: %s\n", infile);
265  return -1;
266  }
267 
268  /* Iterate over all of the blocks of the file */
269  firstPacket = 1;
270  lastSecond = -1;
271  for (block = 0; ; block++)
272  {
274  unsigned long long interval;
275  int numSamples;
276  short *buffer;
277  int i;
278 
279  /* Report progress: minute */
280  if (download->hasGyro)
281  {
282  if (block != 0 && block % 150 == 0) { fprintf(stderr, ":"); } /* Approx. 1 minute of unpacked accel+gyro */
283  }
284  else
285  {
286  if (block != 0 && block % 50 == 0) { fprintf(stderr, "."); } /* Approx. 1 minute of packed accel */
287  }
288  //if (block != 0 && block % 3000 == 0) { fprintf(stderr, "|\n"); } /* Approx. 1 hour */
289  //if (block != 0 && block % 72000 == 0) { fprintf(stderr, "===\n"); } /* Approx. 1 day */
290 
291  /* Read the next block */
292  numSamples = OmReaderNextBlock(reader);
293 
294  /* Successful end of file */
295  if (numSamples == OM_E_FAIL) { break; }
296 
297  /* Problem reading the file */
298  if (numSamples < 0)
299  {
300  fprintf(stderr, "[ERROR: %d - %s]\n", numSamples, OmErrorString(numSamples));
301  errorFile++;
302  break;
303  }
304 
305  /* Get the raw packet handle */
306  dp = OmReaderRawDataPacket(reader);
307 
308  /* Block has no valid data */
309  if (numSamples == 0)
310  {
311  fprintf(stderr, "[WARNING: Missing packet -- probably checksum failed? At block %d, sector %d?]\n", block, block+3);
312  errorFile++;
313  continue;
314  }
315 
316  /* Check the raw packet handle */
317  if (dp == NULL)
318  {
319  fprintf(stderr, "[ERROR: Cannot get raw data packet]\n");
320  errorFile++;
321  continue;
322  }
323 
324  /* Check events mask */
325  if (dp->events & 0xf0)
326  {
327  fprintf(stderr, "[EVENT-ERROR: 0x%02x]\n", dp->events);
328  errorEvent++;
329  }
330 
331  // Read the block start time
332  {
333  unsigned int timestamp;
334  unsigned short fractional;
335  timestamp = OmReaderTimestamp(reader, 0, &fractional);
336  blockStart = Ticks(timestamp, fractional);
337  }
338 
339  {
340  unsigned int light = OmReaderGetValue(reader, OM_VALUE_LIGHT);
341  if (light < minLight) { minLight = light; }
342  }
343 
344  /*
345  fprintf(stderr, "\n[timestampOffset %04d-%02d-%02d %02d:%02d:%02d %+d * %.3f]",
346  OM_DATETIME_YEAR(timestamp), OM_DATETIME_MONTH(timestamp), OM_DATETIME_DAY(timestamp),
347  OM_DATETIME_HOURS(timestamp), OM_DATETIME_MINUTES(timestamp), OM_DATETIME_SECONDS(timestamp),
348  -timestampOffset, 1.0f / sampleRate);
349  */
350 
351  // If we read a block out-of-sequence
352  if (previousSequenceId != (unsigned int)-1 && previousSequenceId + 1 != dp->sequenceId)
353  {
354  if (dp->sequenceId != 0)
355  {
356  fprintf(stderr, "WARNING: Sequence break %u -> %u\n", previousSequenceId, dp->sequenceId);
357  }
358  else
359  {
360  long long recordingLength = (long long)(lastTime - firstTime);
361  long long diff = (long long)(blockStart - previousBlockEnd);
362 
363 #ifdef IGNORE_RECENT_RESTARTS
364  time_t allowed = time(NULL) - IGNORE_RECENT_RESTARTS;
365  struct tm *tm = localtime(&allowed);
366  OM_DATETIME allowedRestartTime = OM_DATETIME_FROM_YMDHMS(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
367 
368  if (dp->timestamp >= allowedRestartTime)
369  {
370  // Recalculate allowed restart time
371 
372  fprintf(stderr, "NOTE: Permitted recording sequence restarted @%u, length of %+.2fs (gap of %+.2fs)\n", previousSequenceId, (float)recordingLength / 65536.0f, (float)diff / 65536.0f);
373  }
374  else
375 #endif
376  {
377  fprintf(stderr, "NOTE: Recording sequence restarted @%u, length of %+.2fs (gap of %+.2fs)\n", previousSequenceId, (float)recordingLength / 65536.0f, (float)diff / 65536.0f);
378  restarts++;
379  breakTime += (float)diff / 65536.0f;
380  }
381 
382  totalDuration += recordingLength;
383  lastBlockStart = 0;
384  firstPacket = 1;
385  lastSecond = -1;
386  firstTime = 0; // Start of a new block
387  }
388  }
389 
390  if (firstTime == 0)
391  {
392  firstTime = blockStart;
393  if (veryFirstTime == 0) { veryFirstTime = firstTime; }
394  lastTime = firstTime;
395  batteryStartPercent = OmReaderGetValue(reader, OM_VALUE_BATTERY_PERCENT);
396  batteryEndPercent = batteryStartPercent;
397  if (batteryMaxPercent < 0) { batteryMaxPercent = batteryStartPercent; }
398  if (batteryMinPercent < 0) { batteryMinPercent = batteryStartPercent; }
399  }
400 
401  if (lastBlockStart > 0)
402  {
403  // Interval
404  interval = blockStart - lastBlockStart;
405 
406  // If the previous block's "blockEnd" is not close (+- 5%) to this block's "blockStart", we have a time discontinuity
407  if (previousBlockEnd != 0 && blockStart != 0 && abs((int)(previousBlockEnd - blockStart)) >= 8000)
408  {
409  long long diff = (long long)(blockStart - previousBlockEnd);
410  fprintf(stderr, "WARNING: Time break in sequence by %+.2f seconds (last: %f, curr: %f)\n", (float)diff / 65536.0f, lastBlockStart / 65536.0f, blockStart / 65536.0f);
411  fprintf(stderr, "\n");
412  breakTime += (float)diff / 65536.0f;
413  }
414  else
415  {
416  // Min/max interval
417  if (first) { first = 0; minInterval = interval; maxInterval = interval; }
418  if (interval > maxInterval) { maxInterval = interval; }
419  if (interval < minInterval) { minInterval = interval; }
420  }
421  }
422 
423  lastBlockStart = blockStart;
424 
425  int numAxes = OmReaderGetValue(reader, OM_VALUE_AXES); // Synchronous axes are [GxGyGz]AxAyAz[[MxMyMz]], 3=A, 6=GA, 9=GAM
426  int scaleAccel = OmReaderGetValue(reader, OM_VALUE_SCALE_ACCEL); // Scaling: number of units for 1g: CWA=256, AX6=2048 (+/-16g), 4096 (+/-8g), 8192 (+/-4g), 16384 (+/-2g)
427  int scaleGyro = OmReaderGetValue(reader, OM_VALUE_SCALE_GYRO); // Scaling: number of degrees per second that (2^15=)32768 represents: AX6= 2000, 1000, 500, 250, 125, 0=off.
428  int axisAccel = OmReaderGetValue(reader, OM_VALUE_ACCEL_AXIS);
429  int axisGyro = OmReaderGetValue(reader, OM_VALUE_GYRO_AXIS);
430 
431  unsigned int rateCode = OmReaderGetValue(reader, OM_VALUE_SAMPLERATE);
432  unsigned int rate = (3200 / (1 << (15 - (rateCode & 0x0f))));
433  int minPacketCount = rate - (12 * rate / 100); // -12%
434  int maxPacketCount = rate + (12 * rate / 100); // +12%
435 
436  buffer = OmReaderBuffer(reader);
437  for (i = 0; i < numSamples; i++)
438  {
439  OM_DATETIME dateTime;
440  unsigned short fractional;
441  short x, y, z;
442  double v;
443 
444  totalSamples++;
445 
446  /* Get the date/time value for this sample, and the 1/65536th fractions of a second */
447  dateTime = OmReaderTimestamp(reader, i, &fractional);
448  sprintf(timeString, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
449  OM_DATETIME_YEAR(dateTime), OM_DATETIME_MONTH(dateTime), OM_DATETIME_DAY(dateTime),
450  OM_DATETIME_HOURS(dateTime), OM_DATETIME_MINUTES(dateTime), OM_DATETIME_SECONDS(dateTime),
451  (int)fractional * 1000 / 0xffff);
452 
453  /* Get the accel x/y/z/ values */
454  x = buffer[numAxes * i + axisAccel + 0];
455  y = buffer[numAxes * i + axisAccel + 1];
456  z = buffer[numAxes * i + axisAccel + 2];
457 
458  /* Get the accel x/y/z/ values */
459  short gx = 0, gy = 0, gz = 0;
460  bool hasGyro = (axisGyro >= 0);
461  if (hasGyro)
462  {
463  gx = buffer[numAxes * i + axisGyro + 0];
464  gy = buffer[numAxes * i + axisGyro + 1];
465  gz = buffer[numAxes * i + axisGyro + 2];
466  download->hasGyro = true;
467  }
468 
469  /* Detect stuck values */
470  if (x == lx && y == ly && z == lz)
471  {
472  stuck++;
473  if (stuck == STUCK_COUNT)
474  {
475  fprintf(stderr, "\nERROR: Readings could be stuck at (%d, %d, %d); has been for %d consecutive readings. ", lx, ly, lz, stuck);
476  errorStuck++;
477  }
478  }
479  else
480  {
481  if (stuck >= STUCK_COUNT)
482  {
483  fprintf(stderr, "\nNOTE: Readings now changed -- were at (%d, %d, %d) for total of %d consecutive readings. ", lx, ly, lz, stuck);
484  }
485  lx = x; ly = y; lz = z;
486  stuck = 0;
487  }
488 
489  /* Detect gyro stuck values */
490  if (hasGyro)
491  {
492  if (gx == lgx && gy == lgy && gz == lgz)
493  {
494  gstuck++;
495  if (gstuck == STUCK_COUNT)
496  {
497  fprintf(stderr, "\nERROR: Gyro readings could be stuck at (%d, %d, %d); has been for %d consecutive readings. ", lgx, lgy, lgz, gstuck);
498  errorStuck++;
499  }
500  }
501  else
502  {
503  if (stuck >= STUCK_COUNT)
504  {
505  fprintf(stderr, "\nNOTE: Gyro readings now changed -- were at (%d, %d, %d) for total of %d consecutive readings. ", lgx, lgy, lgz, gstuck);
506  }
507  lgx = gx; lgy = gy; lgz = gz;
508  gstuck = 0;
509  }
510  }
511 
512  /* Get the SVM - 1 */
513  v = sqrt((x / (double)scaleAccel) * (x / (double)scaleAccel) + (y / (double)scaleAccel) * (y / (double)scaleAccel) + (z / (double)scaleAccel) * (z / (double)scaleAccel)) - 1.0;
514  av = ((1.0 - AVERAGE_FACTOR) * av) + (AVERAGE_FACTOR * v);
515  if (fabs(av) > peakAv) { peakAv = fabs(av); }
516  if (fabs(av) > maxAv) { maxAv = fabs(av); }
517  if (fabs(av) > AVERAGE_RANGE_MAX)
518  {
519  if (!outOfRange)
520  {
521  errorRange++;
522  outOfRange = 1;
523  peakAv = fabs(av);
524  fprintf(stderr, "WARNING: Average SVM gone out-of-normal-range abs(avg(svm-1)): %0.3f\n", fabs(av));
525  }
526  }
527  else if (fabs(av) < AVERAGE_RANGE_OFF && outOfRange)
528  {
529  outOfRange = 0;
530  fprintf(stderr, "WARNING: Average SVM back in normal range, peak was abs(avg(svm-1)): %0.3f\n", peakAv);
531  }
532 
533  /* Output the data */
534 // if (output) { fprintf(stdout, "%s,%d,%d,%d\n", timeString, x, y, z); }
535 
536  {
537  int hour = OM_DATETIME_HOURS(dateTime);
538  if (hour != lastHour)
539  {
540  fprintf(stderr, "\n%02d-%02d %02d:%02d:%02d.%02d", /*OM_DATETIME_YEAR(dateTime) % 100, */ OM_DATETIME_MONTH(dateTime), OM_DATETIME_DAY(dateTime), OM_DATETIME_HOURS(dateTime), OM_DATETIME_MINUTES(dateTime), OM_DATETIME_SECONDS(dateTime), (int)fractional * 100 / 0xffff);
541  lastHour = hour;
542  }
543  }
544 
545  seconds = OM_DATETIME_SECONDS(dateTime);
546  if (lastSecond == -1) { lastSecond = seconds; };
547  if (seconds != lastSecond)
548  {
549  if (firstPacket) { /* fprintf(stderr, "!"); */ firstPacket = 0; }
550  else if (packetCount >= minPacketCount && packetCount <= maxPacketCount) { /* fprintf(stderr, "#"); */ }
551  else
552  {
553  if (seconds == lastSecond + 1 || (seconds == 0 && lastSecond == 59))
554  {
555  errorRate++;
556  fprintf(stderr, "WARNING: 'Out of range' %d samples in second :%02d before %s]\n", packetCount, lastSecond, timeString);
557  }
558  else
559  {
560  errorBreaks++;
561  fprintf(stderr, "WARNING: Non-sequential second jump (%d samples in second :%02d before %s)]\n", packetCount, lastSecond, timeString);
562  }
563  }
564  lastSecond = seconds;
565  packetCount = 0;
566  }
567  packetCount++;
568 
569  }
570 
571  // Store the current sequence id
572  previousSequenceId = dp->sequenceId;
573 
574  // Read the block end time
575  {
576  unsigned int timestamp;
577  unsigned short fractional;
578  timestamp = OmReaderTimestamp(reader, numSamples, &fractional);
579  previousBlockEnd = Ticks(timestamp, fractional);
580  }
581 
582  lastTime = previousBlockEnd;
583  batteryEndPercent = OmReaderGetValue(reader, OM_VALUE_BATTERY_PERCENT);
584  if (batteryEndPercent > batteryMaxPercent) { batteryMaxPercent = batteryEndPercent; }
585  if (batteryEndPercent < batteryMinPercent) { batteryMinPercent = batteryEndPercent; }
586 
587  }
588 
589  /* Last session's duration */
590  {
591  long long recordingLength = (long long)(lastTime - firstTime);
592  totalDuration += recordingLength;
593  }
594 
595  /* Summary */
596  {
597  int startStopFail = 0;
598 
599  hp = OmReaderRawHeaderPacket(reader);
600 
601  fprintf(stderr, "\n");
602 
603  if (hp == NULL)
604  {
605  fprintf(stderr, "ERROR: Unable to access file header for start/stop times.\n");
606  startStopFail += 4;
607  }
608  else
609  {
611  {
612  unsigned long fStart = (unsigned long)TimeSerial(hp->loggingStartTime);
613  unsigned long aStart = (unsigned long)(veryFirstTime >> 16);
614  int startDiff = (int)(aStart - fStart);
615  if (abs(startDiff) < 80)
616  {
617  fprintf(stderr, "NOTE: Data started near recording start time (%ds).\n", startDiff);
618  }
619  else
620  {
621  fprintf(stderr, "ERROR: Data did not start near recording start time (%ds).\n", startDiff);
622  startStopFail += 2;
623  }
624  }
625  else
626  {
627  fprintf(stderr, "NOTE: Recording has no start time (not verifying).\n");
628  }
629 
630 
632  {
633  unsigned long fEnd = (unsigned long)TimeSerial(hp->loggingEndTime);
634  unsigned long aEnd = (unsigned long)(lastTime >> 16);
635  int stopDiff = (int)(aEnd - fEnd);
636  if (globalOptions & VERIFY_OPTION_NO_CHECK_STOP)
637  {
638  fprintf(stderr, "NOTE: Ignoring whether data stopped near recording stop time (difference was %ds).\n", stopDiff);
639  }
640  else if (abs(stopDiff) < 80)
641  {
642  fprintf(stderr, "NOTE: Data stopped near recording stop time (%ds).\n", stopDiff);
643  }
644  else
645  {
646  fprintf(stderr, "ERROR: Data did not stop near recording stop time (%ds).\n", stopDiff);
647  startStopFail += 1;
648  }
649  }
650  else
651  {
652  fprintf(stderr, "NOTE: Recording has no stop time (not verifying).\n");
653  }
654  }
655 
656 // Error mask
657 #define CODE_ERROR_MASK 0xfffff000
658 #define CODE_WARNING_MASK 0x00000fff
659 
660 // FILE - Read file issue (sector incorrect or checksum mismatch)
661 #define CODE_WARNING_FILE 0x000001
662 #define CODE_ERROR_FILE 0x001000
663 
664 // EVENT - System logged event error (e.g. FIFO overflow)
665 #define CODE_WARNING_EVENT 0x000002
666 #define CODE_ERROR_EVENT 0x002000
667 
668 // STUCK - Where the accelerometer appears to be stuck at the same value
669 #define CODE_WARNING_STUCK 0x000004
670 #define CODE_ERROR_STUCK 0x004000
671 
672 // RANGE - Where the accelerometer doesn't seem to average to 1G
673 #define CODE_WARNING_RANGE 0x000008
674 #define CODE_ERROR_RANGE 0x008000
675 
676 // RATE - Where the rate is too far off 100Hz
677 #define CODE_WARNING_RATE 0x000010
678 #define CODE_ERROR_RATE 0x010000
679 
680 // BREAKS - Where there is a gap in the data
681 #define CODE_WARNING_BREAKS 0x000020
682 #define CODE_ERROR_BREAKS 0x020000
683 
684 // RESTARTS - Where the device has restarted
685 #define CODE_WARNING_RESTARTS 0x000040
686 #define CODE_ERROR_RESTARTS 0x040000
687 
688 // LIGHT - Where the light level reading is far lower than normal (seems to indicate a failure)
689 #define CODE_WARNING_LIGHT 0x000080
690 #define CODE_ERROR_LIGHT 0x080000
691 
692 // BATT - Where the battery discharge is more rapid than usual
693 #define CODE_WARNING_BATT 0x000100
694 #define CODE_ERROR_BATT 0x100000
695 
696 // STARTSTOP - Where the configured start/stop times are not met
697 #define CODE_WARNING_STARTSTOP 0x000200
698 #define CODE_ERROR_STARTSTOP 0x200000
699 
700 // NANDHEALTH - Where the number of reported spare blocks are low
701 #define CODE_WARNING_NANDHEALTH 0x000400
702 #define CODE_ERROR_NANDHEALTH 0x400000
703 
704 // NANDID - Where the NAND id is unexpected
705 #define CODE_WARNING_NANDID 0x000800
706 #define CODE_ERROR_NANDID 0x800000
707 
708 
709 retval = 0;
710 if (errorFile > 0) { retval |= CODE_ERROR_FILE; } else if (errorFile > 0) { retval |= CODE_WARNING_FILE; }
711 if (errorEvent > 0) { retval |= CODE_ERROR_EVENT; } else if (errorEvent > 0) { retval |= CODE_WARNING_EVENT; }
712 if (errorStuck > 0) { retval |= CODE_ERROR_STUCK; } else if (errorStuck > 0) { retval |= CODE_WARNING_STUCK; }
713 if (errorRange > 0) { retval |= CODE_ERROR_RANGE; } else if (errorRange > 0) { retval |= CODE_WARNING_RANGE; }
714 if (errorRate > 0) { retval |= CODE_ERROR_RATE; } else if (errorRate > 0) { retval |= CODE_WARNING_RATE; }
715 if (errorBreaks > 0) { retval |= CODE_ERROR_BREAKS; } else if (errorBreaks > 0) { retval |= CODE_WARNING_BREAKS; }
716 if (restarts > globalAllowedRestarts) { retval |= CODE_ERROR_RESTARTS; } else if (restarts > 0) { retval |= CODE_WARNING_RESTARTS; }
717 if (!download->hasGyro)
718 {
719  // Only use light level on original AX3
720  if (minLight < 90) { retval |= CODE_ERROR_LIGHT; } else if (minLight < 140) { retval |= CODE_WARNING_LIGHT; }
721 }
722 // Discharge
723 {
724  float hours = ((totalDuration >> 16) / 60.0f / 60.0f);
725  float percentLoss = (float)batteryMaxPercent - batteryMinPercent;
726  percentPerHour = 0;
727  if (hours > 0) { percentPerHour = percentLoss / hours; } else { percentPerHour = 0; }
728  if (download->hasGyro)
729  {
730  // at least 7-days: 0.595%; at least 8-days: 0.521%
731  if (percentPerHour >= 0.595f) { retval |= CODE_ERROR_BATT; } else if (percentPerHour >= 0.521f) { retval |= CODE_WARNING_BATT; } // Gyro levels
732  }
733  else
734  {
735  if (percentPerHour >= 0.29f) { retval |= CODE_ERROR_BATT; } else if (percentPerHour >= 0.25f) { retval |= CODE_WARNING_BATT; } // Accel-only levels
736  }
737 }
738 // Start/stop
739 if (startStopFail) { retval |= CODE_ERROR_STARTSTOP; }
740 
741  fprintf(stderr, "---\n");
742  fprintf(stderr, "Input file,%d,\"%s\",%d\n", id, infile, retval);
743  fprintf(stderr, "Summary errors: file=%d, event=%d, stuck=%d, range=%d, rate=%d, breaks=%d\n", errorFile, errorEvent, errorStuck, errorRange, errorRate, errorBreaks);
744  fprintf(stderr, "Summary info-1: restart=%d, breakTime=%0.1fs, maxAv=%f\n", restarts, breakTime, maxAv);
745  fprintf(stderr, "Summary info-2: minInterval=%0.3f, maxInterval=%0.3f, duration=%0.4fh\n", minInterval / 65536.0f, maxInterval / 65536.0f, ((totalDuration >> 16) / 60.0f / 60.0f));
746  fprintf(stderr, "Summary info-3: minLight=%d, Bmax=%d%%, Bmin=%d%%, intervalFail=%d\n", minLight, batteryMaxPercent, batteryMinPercent, startStopFail);
747 
748 if (download != NULL)
749 {
750  // Spare blocks
751  if (download->memoryHealth < 0) { ; }
752  else if (download->memoryHealth < OM_MEMORY_HEALTH_ERROR) { retval |= CODE_ERROR_NANDHEALTH; } // ERROR: No spare blocks
753  else if (download->memoryHealth < OM_MEMORY_HEALTH_WARNING) { retval |= CODE_WARNING_NANDHEALTH; } // WARNING: Very few spare blocks
754 
755 #ifdef ID_NAND
756  // NAND ID
757  if (download->nandType < 0) { ; } // Firmware doesn't support nand Id
758  else if (download->nandType < 1) { retval |= CODE_ERROR_NANDID; } // ERROR: Not primary or secondary type
759  //else if (download->nandType != 1) { retval |= CODE_WARNING_NANDID; } // WARNING: Not primary type
760 
761  fprintf(stderr, "NAND #%d (%d spare)\n", download->nandType, download->memoryHealth);
762 #endif
763 }
764 
765  fprintf(stderr, "---\n");
766 
767 #define HEADER "VERIFY," "id," "summary," "file," "event," "stuck," "range," "rate," "breaks," "restarts," "breakTime," "maxAv," "minInterval," "maxInterval," "duration," "minLight," "batteryMaxPercent," "batteryMinPercent," "intervalFail," "percentLoss," "description\n"
768  {
769  description[0] = '\0';
770 
771  if (retval & CODE_WARNING_FILE ) { strcat(description, "W:File;"); }
772  if (retval & CODE_WARNING_EVENT ) { strcat(description, "W:Event;"); }
773  if (retval & CODE_WARNING_STUCK ) { strcat(description, "W:Stuck;"); }
774  if (retval & CODE_WARNING_RANGE ) { strcat(description, "W:Range;"); }
775  if (retval & CODE_WARNING_RATE ) { strcat(description, "W:Rate;"); }
776  if (retval & CODE_WARNING_BREAKS ) { strcat(description, "W:Breaks;"); }
777  if (retval & CODE_WARNING_RESTARTS ) { strcat(description, "W:Restarts;"); }
778  if (retval & CODE_WARNING_LIGHT ) { char temp[32]; sprintf(temp, "W:Light(%d);", minLight); strcat(description, temp); }
779  if (retval & CODE_WARNING_BATT ) { char temp[32]; sprintf(temp, "W:Batt(%2.2f);", percentPerHour); strcat(description, temp); }
780  if (retval & CODE_WARNING_STARTSTOP ) { strcat(description, "W:StartStop;"); }
781  if (retval & CODE_WARNING_NANDHEALTH) { strcat(description, "W:NandHealth;"); }
782  if (retval & CODE_WARNING_NANDID ) { strcat(description, "W:NandId;"); }
783  if (retval & CODE_ERROR_FILE ) { strcat(description, "E:File;"); }
784  if (retval & CODE_ERROR_EVENT ) { strcat(description, "E:Event;"); }
785  if (retval & CODE_ERROR_STUCK ) { strcat(description, "E:Stuck;"); }
786  if (retval & CODE_ERROR_RANGE ) { strcat(description, "E:Range;"); }
787  if (retval & CODE_ERROR_RATE ) { strcat(description, "E:Rate;"); }
788  if (retval & CODE_ERROR_BREAKS ) { strcat(description, "E:Breaks;"); }
789  if (retval & CODE_ERROR_RESTARTS ) { strcat(description, "E:Restarts;"); }
790  if (retval & CODE_ERROR_LIGHT ) { char temp[32]; sprintf(temp, "E:Light(%d);", minLight); strcat(description, temp); }
791  if (retval & CODE_ERROR_BATT ) { char temp[32]; sprintf(temp, "E:Batt(%2.2f);", percentPerHour); strcat(description, temp); }
792  if (retval & CODE_ERROR_STARTSTOP ) { strcat(description, "E:StartStop;"); }
793  if (retval & CODE_ERROR_NANDHEALTH ) { strcat(description, "E:NandHealth;"); }
794  if (retval & CODE_ERROR_NANDID ) { strcat(description, "E:NandId;"); }
795 
796  sprintf(line, "VERIFYX," "%s," "%d," "%d," "%d," "%d," "%d," "%d," "%d," "%d," "%.1f," "%.4f," "%.3f," "%.3f," "%.4f," "%d," "%d," "%d," "%d," "%f," "%s\n",
797  label, retval, errorFile, errorEvent, errorStuck, errorRange, errorRate, errorBreaks, restarts, breakTime, maxAv, minInterval / 65536.0f, maxInterval / 65536.0f, ((totalDuration >> 16) / 60.0f / 60.0f), minLight, batteryMaxPercent, batteryMinPercent, startStopFail, percentPerHour, description);
798  }
799 
800  fprintf(stdout, line);
801  fprintf(stderr, line);
802  if (outfile != NULL)
803  {
804  // New output format
805  if (globalOptions & VERIFY_OPTION_OUTPUT_NEW)
806  {
807  int passed = ((retval & CODE_ERROR_MASK) == 0) ? 1 : 0;
808  // "VERIFY,YYYY-MM-DD hh:mm:ss.000,12345,1,260,W:Batt;W:Stuck;"
809  sprintf(line, "VERIFY,%s,%s,%d,%d,%s\n", formattedtime(now()), label, passed, retval, description);
810 
811  fprintf(stderr, line);
812  }
813 
814  fprintf(stdout, line);
815 
816  fprintf(outfile, line);
817  fflush(outfile);
818  }
819  printf(line);
820  }
821 
822  /* Close the files */
823  OmReaderClose(reader);
824  return retval;
825 }
826 
827 
828 
829 /* Download updated */
830 static void verify_DownloadCallback(void *reference, int deviceId, OM_DOWNLOAD_STATUS status, int value)
831 {
832  download_t *download = (download_t *)reference;
833 
834  if (status == OM_DOWNLOAD_PROGRESS)
835  {
836  //printf("VERIFY #%d: Progress %d%%.\n", deviceId, value);
837  if ((value % 5) == 0) { printf("<#%d@%d%%>", deviceId, value); } // report every 5%
838  }
839  else if (status == OM_DOWNLOAD_COMPLETE)
840  {
841  const char *file = download->filename;
842  int verifyResult;
843  int result;
844 
845  printf("VERIFY #%d: Download complete, verify starting... (options 0x%04x)\n", deviceId, globalOptions);
846 
847  verifyResult = verify_process(deviceId, file, download, globalOptions);
848 
849  if (verifyResult < 0)
850  {
851  char line[128];
852  sprintf(line, "#ERROR,%s,%d,Verify failed (%d)\n", formattedtime(now()), deviceId, verifyResult);
853  fprintf(stderr, line);
854  fprintf(stdout, line);
855  if (outfile != NULL) { fprintf(outfile, line); fflush(outfile); }
856  OmSetLed(deviceId, LED_ERROR_COMMS);
857  }
858  else if (verifyResult & CODE_ERROR_MASK)
859  {
860  OmSetLed(deviceId, LED_FAILED); // Looks like a problem
861  }
862  else
863  {
864  /* Set the session id (use the deviceId) */
865  result = OmSetSessionId(deviceId, 0);
866  fprintf(stderr, "VERIFY #%d: Setting session id: %u\n", deviceId, 0);
867  if (OM_FAILED(result)) { fprintf(stderr, "ERROR: OmSetSessionId() %s\n", OmErrorString(result)); }
868 
869  /* Set the delayed start/stop times */
870  fprintf(stderr, "VERIFY #%d: Setting start/stop: INFINITY/ZERO\n", deviceId);
872  if (OM_FAILED(result)) { fprintf(stderr, "ERROR: OmSetDelays() %s\n", OmErrorString(result)); }
873 
874  /* Commit the new settings */
875  fprintf(stderr, "VERIFY #%d: Committing new settings...\n", deviceId);
876  result = OmEraseDataAndCommit(deviceId, OM_ERASE_QUICKFORMAT);
877  if (OM_FAILED(result)) { fprintf(stderr, "ERROR: OmEraseDataAndCommit() %s\n", OmErrorString(result)); }
878 
879  // Set LEDs
880  if (verifyResult & CODE_WARNING_MASK) { OmSetLed(deviceId, LED_WARNING); } // Warning
881  else { OmSetLed(deviceId, LED_OK); } // Everything ok
882  }
883 
884  }
885  else if (status == OM_DOWNLOAD_CANCELLED)
886  {
887  char line[128];
888  sprintf(line, "#ERROR,%s,%d,Download cancelled\n", formattedtime(now()), deviceId);
889  fprintf(stderr, line);
890  fprintf(stdout, line);
891  if (outfile != NULL) { fprintf(outfile, line); fflush(outfile); }
892  OmSetLed(deviceId, LED_ERROR_COMMS);
893  }
894  else if (status == OM_DOWNLOAD_ERROR)
895  {
896  char line[128];
897  sprintf(line, "#ERROR,%s,%d,Download error (diagnostic 0x%04x)\n", formattedtime(now()), deviceId, value);
898  fprintf(stderr, line);
899  fprintf(stdout, line);
900  if (outfile != NULL) { fprintf(outfile, line); fflush(outfile); }
901  OmSetLed(deviceId, LED_ERROR_COMMS);
902  }
903  else
904  {
905  char line[128];
906  sprintf(line, "#ERROR,%s,%d,Unexpected status %d / 0x%04x\n", formattedtime(now()), deviceId, status, value);
907  fprintf(stderr, line);
908  fprintf(stdout, line);
909  if (outfile != NULL) { fprintf(outfile, line); fflush(outfile); }
910  OmSetLed(deviceId, LED_ERROR_COMMS);
911  }
912 
913 
914  // On any download event other than progress, alert the user
915  if (status != OM_DOWNLOAD_PROGRESS)
916  {
917 #ifdef _WIN32
918  {
919  char oldTitle[2048];
920  char newTitle[] = "[ALERT]";
921  HWND hWnd = NULL;
922  FLASHWINFO fwi = {0};
923  GetConsoleTitle(oldTitle, sizeof(oldTitle));
924  SetConsoleTitle(newTitle);
925 
926  fprintf(stderr, "\a");
927  MessageBeep(0xffffffff);
928  Sleep(40);
929 
930  hWnd = FindWindow(NULL, newTitle);
931  SetConsoleTitle(oldTitle);
932  if (hWnd != NULL)
933  {
934  fwi.cbSize = sizeof(fwi);
935  fwi.hwnd = hWnd;
936  fwi.dwFlags = FLASHW_ALL;
937  fwi.uCount = 3;
938  fwi.dwTimeout = 0;
939  FlashWindowEx(&fwi);
940  }
941  }
942 #else
943  fprintf(stderr, "\a");
944 #endif
945  }
946 
947  return;
948 }
949 
950 
951 /* Device updated */
952 static void verify_DeviceCallback(void *reference, int deviceId, OM_DEVICE_STATUS status)
953 {
954  if (status == OM_DEVICE_CONNECTED)
955  {
956  int result;
957  int dataBlockSize = 0, dataOffsetBlocks = 0, dataNumBlocks = 0;
958  OM_DATETIME startTime = 0, endTime = 0;
959 
960  printf("VERIFY #%d: Device CONNECTED (verify options 0x%04x)\n", deviceId, globalOptions);
961 
962  /* Get the data range */
963  result = OmGetDataRange(deviceId, &dataBlockSize, &dataOffsetBlocks, &dataNumBlocks, &startTime, &endTime);
964  if (OM_FAILED(result)) { printf("ERROR: OmGetDataRange() %s\n", OmErrorString(result)); return; }
965  printf("VERIFY #%d: %d blocks data, at offset %d blocks (1 block = %d bytes)\n", deviceId, dataNumBlocks, dataOffsetBlocks, dataBlockSize);
966  /* Display the data start and end times */
967  {
968  char startString[OM_DATETIME_BUFFER_SIZE], endString[OM_DATETIME_BUFFER_SIZE];
969  OmDateTimeToString(startTime, startString);
970  OmDateTimeToString(endTime, endString);
971  printf("VERIFY #%d: ... %s --> %s\n", deviceId, startString, endString);
972  }
973 
974  /* Create reference handle */
975  download_t* download = (download_t*)malloc(sizeof(download_t));
976  memset(download, 0, sizeof(download_t));
977  const char* downloadPath = ".";
978 
979  /* Get NAND information */
980  download->memoryHealth = OmGetMemoryHealth(deviceId);
981 #ifdef ID_NAND
982  download->nandType = -1;
983  memset(download->nandId, 0, sizeof(download->nandId));
984  result = OmGetNandId(deviceId, download->nandId, NULL, &download->nandType);
985  if (result == OM_E_NOT_IMPLEMENTED) { fprintf(stderr, "NOTE: This firmware doesn't support NANDID command\n"); }
986  else if (result == OM_E_UNEXPECTED_RESPONSE) { fprintf(stderr, "NOTE: Unexpected NANDID response (firmware probably doesn't support NANDID command)\n"); }
987 #endif
988 
989  /* Allocate filename string */
990  download->filename = (char*)malloc(strlen(downloadPath) + 64); /* downloadPath + "/4294967295-65535.cwa" + 1 (NULL-terminated) */
991  /* Copy path, and platform-specific path separator char */
992  strcpy(download->filename, downloadPath);
993 #ifdef _WIN32
994  if (download->filename[strlen(download->filename) - 1] != '\\') strcat(download->filename, "\\");
995 #else
996  if (download->filename[strlen(download->filename) - 1] != '/') strcat(download->filename, "/");
997 #endif
998 
999  /* Append session-id and device-id as part of the filename */
1000  if (deviceId >= 0 && deviceId <= 9999)
1001  {
1002  sprintf(download->filename + strlen(download->filename), "CWA-%04u.CWA", deviceId);
1003  }
1004  else if (deviceId >= 0 && deviceId <= 9999999)
1005  {
1006  sprintf(download->filename + strlen(download->filename), "CWA-%07u.CWA", deviceId);
1007  }
1008  else
1009  {
1010  sprintf(download->filename + strlen(download->filename), "CWA-%u.CWA", deviceId);
1011  }
1012 
1013  /* Check if there's any data blocks stored (not just the headers) */
1014  if (dataNumBlocks - dataOffsetBlocks <= 0)
1015  {
1016  // TODO: fstat for date/time difference?
1017  if (access(download->filename, 0))
1018  {
1019  printf("VERIFY #%d: Ignoring - no data stored, but local file already downloaded (manually check recent log): %s\n", deviceId, download->filename);
1020  //OmSetLed(deviceId, LED_ERROR_COMMS); // Error accessing data
1021  }
1022  else
1023  {
1024  printf("VERIFY #%d: Ignoring - no data stored (no file already downloaded)\n", deviceId);
1025  OmSetLed(deviceId, LED_ERROR_COMMS); // Error accessing data
1026  }
1027  free(download); // TODO: If download->filename exists, call verify_DownloadCallback(OM_DOWNLOAD_COMPLETE, deviceId, OM_DOWNLOAD_COMPLETE, -1); // would need to do this in another thread though!
1028  }
1029  else
1030  {
1031  /* Begin download */
1032  OmSetLed(deviceId, LED_PROCESSING);
1033  printf("VERIFY #%d: Starting download to file: %s\n", deviceId, download->filename);
1034  {
1035  result = OmBeginDownloadingReference(deviceId, 0, -1, download->filename, download);
1036  }
1037  if (OM_FAILED(result)) { printf("ERROR: OmBeginDownloading() %s\n", OmErrorString(result)); }
1038 
1039  /* Leave filename string for download complete to free... */
1040  }
1041  }
1042  else if (status == OM_DEVICE_REMOVED)
1043  {
1044  printf("VERIFY #%d: Device REMOVED\n", deviceId);
1045  /* The download will have already been cancelled in the event of a device removal */
1046  /*OmCancelDownload(deviceId);*/
1047  }
1048  else
1049  {
1050  printf("VERIFY #%d: Error, unexpected status %d\n", deviceId, status);
1051  OmSetLed(deviceId, LED_ERROR_COMMS); // Error accessing data
1052  }
1053 
1054  return;
1055 }
1056 
1057 
1058 
1059 /* Record function */
1060 int verify(void)
1061 {
1062  int result;
1063 
1064  /* Set device callback before API startup to get initially-connected devices through the callback */
1065  OmSetDeviceCallback(verify_DeviceCallback, NULL);
1066 
1067  /* Set download callback */
1068  OmSetDownloadCallback(verify_DownloadCallback, NULL);
1069 
1070  /* Start the API */
1071  result = OmStartup(OM_VERSION);
1072  if (OM_FAILED(result)) { fprintf(stderr, "ERROR: OmStartup() %s\n", OmErrorString(result)); return -1; }
1073 
1074  for (;;)
1075  {
1076  /* Wait 5 seconds */
1077  Sleep(60000);
1078  fprintf(stderr, "<.>");
1079  }
1080 
1081  /* Shutdown the API */
1082  result = OmShutdown();
1083  if (OM_FAILED(result)) { fprintf(stderr, "ERROR: OmShutdown() %s\n", OmErrorString(result)); return -1; }
1084 
1085  /* Close the input and output files */
1086  if (outfile != NULL) { fclose(outfile); }
1087 }
1088 
1089 
1090 /* Main function */
1091 int verify_main(int argc, char *argv[])
1092 {
1093  const char *outfilename = NULL;
1094  int ret = -1;
1095  int i;
1096  fprintf(stderr, "VERIFY: verify a specified binary data file contains sensible data.\n");
1097  fprintf(stderr, "\n");
1098  globalOptions = VERIFY_OPTION_OUTPUT_NEW;
1099  if (argc > 1)
1100  {
1101  const char *infile = NULL;
1102  //char output = 0;
1103 
1104  for (i = 1; i < argc; i++)
1105  {
1106  if (!strcmp(argv[i], "-headeronly"))
1107  {
1108  fprintf(stdout, HEADER);
1109  return -2;
1110  }
1111  else if (!strcmp(argv[i], "-stop-clear-all")) { fprintf(stderr, "VERIFY: Option -stop-clear-all\n"); globalOptions |= VERIFY_OPTION_ALL; }
1112  else if (!strcmp(argv[i], "-no-check-stop")) { fprintf(stderr, "VERIFY: Option -no-check-stop\n"); globalOptions |= VERIFY_OPTION_NO_CHECK_STOP; }
1113  else if (!strcmp(argv[i], "-output:new")) { fprintf(stderr, "VERIFY: Option -output:new\n"); globalOptions |= VERIFY_OPTION_OUTPUT_NEW; }
1114  else if (!strcmp(argv[i], "-output:old")) { fprintf(stderr, "VERIFY: Option -output:old\n"); globalOptions &= ~VERIFY_OPTION_OUTPUT_NEW; }
1115  else if (!strcmp(argv[i], "-allow-restarts")) { globalAllowedRestarts = atoi(argv[++i]); fprintf(stderr, "VERIFY: Option -allow-restarts %d\n", globalAllowedRestarts); }
1116  else if (argv[i][0] == '-')
1117  {
1118  fprintf(stdout, "ERROR: Unrecognized option %s\n", argv[i]);
1119  return -3;
1120  }
1121  else if (infile == NULL && !(globalOptions & VERIFY_OPTION_ALL))
1122  {
1123  infile = argv[i];
1124  }
1125  else if (outfilename == NULL)
1126  {
1127  outfilename = argv[i];
1128  }
1129  else
1130  {
1131  fprintf(stdout, "ERROR: Unexpected parameter %s\n", argv[i]);
1132  return -3;
1133  }
1134  }
1135 
1136  /* Open the input and output files */
1137  if (outfilename != NULL)
1138  {
1139  outfile = fopen(outfilename, "at");
1140  }
1141 
1142  //if (argc > 2 && !strcmp(argv[2], "-output")) { output = 1; }
1143  if ((globalOptions & VERIFY_OPTION_ALL) == 0)
1144  {
1145  download_t download = { 0 };
1146  download.nandType = -1; // NAND ID not supported from data files
1147  download.memoryHealth = -1; // memory health not supported from data files
1148  ret = verify_process(-1, infile, &download, globalOptions);
1149  }
1150  else
1151  {
1152  verify();
1153  }
1154 
1155  if (outfile != NULL) { fclose(outfile); }
1156  }
1157  else
1158  {
1159  fprintf(stderr, "Usage: verify <<binary-input-file> | <-stop-clear-all> [outfile.csv] | <-headeronly>> [-no-check-stop] [-allow-restarts <n>]\n");
1160  fprintf(stderr, "\n");
1161  fprintf(stderr, "Where: binary-input-file: the name of the binary file to verify.\n");
1162  fprintf(stderr, "\n");
1163  fprintf(stderr, "Example: verify data.cwa\n");
1164  fprintf(stderr, "\n");
1165  }
1166 
1167 #if defined(_WIN32) && defined(_DEBUG)
1168  if (IsDebuggerPresent()) { fprintf(stderr, "Press [enter] to exit..."); getc(stdin); }
1169 #endif
1170 
1171  return ret;
1172 }
1173 
CODE_ERROR_RATE
#define CODE_ERROR_RATE
OmSetSessionId
int OmSetSessionId(int deviceId, unsigned int sessionId)
Sets the specified device's session identifier to be used at the next recording session.
OM_DATETIME_FROM_YMDHMS
#define OM_DATETIME_FROM_YMDHMS(year, month, day, hours, minutes, seconds)
Macro to create a packed date/time value from components.
Definition: omapi.h:1073
OM_DATETIME_INFINITE
#define OM_DATETIME_INFINITE
Special date/time value for "infinitely late".
Definition: omapi.h:1090
CODE_ERROR_EVENT
#define CODE_ERROR_EVENT
OM_DEVICE_REMOVED
Device is being removed, or is already removed.
Definition: omapi.h:289
CODE_ERROR_NANDHEALTH
#define CODE_ERROR_NANDHEALTH
OM_DEVICE_CONNECTED
Device has been connected.
Definition: omapi.h:290
CODE_ERROR_STUCK
#define CODE_ERROR_STUCK
now
unsigned long long now(void)
Definition: verify.c:180
OmSetDeviceCallback
int OmSetDeviceCallback(OmDeviceCallback deviceCallback, void *reference)
Sets the callback function that is called whenever a device is added or removed.
VERIFY_OPTION_OUTPUT_NEW
#define VERIFY_OPTION_OUTPUT_NEW
Definition: verify.c:80
OmErrorString
const char * OmErrorString(int status)
Returns an error string for the specified API return code.
OM_DATETIME_MIN_VALID
#define OM_DATETIME_MIN_VALID
The minimum valid date/time value.
Definition: omapi.h:1087
LED_WARNING
#define LED_WARNING
Definition: verify.c:73
omapi.h
Open Movement API.
verify_main
int verify_main(int argc, char *argv[])
Definition: verify.c:1091
OmGetMemoryHealth
int OmGetMemoryHealth(int deviceId)
Determine the health of the NAND flash memory on the specified device.
OM_DATETIME
unsigned long OM_DATETIME
Definition: omapi.h:332
AVERAGE_RANGE_OFF
#define AVERAGE_RANGE_OFF
Definition: verify.c:86
OM_READER_HEADER_PACKET::loggingStartTime
OM_DATETIME loggingStartTime
@13 +4 Start time for delayed logging
Definition: omapi.h:1300
OM_VALUE_SCALE_GYRO
Scaling: number of degrees per second that (2^15=)32768 represents: AX6= 2000, 1000,...
Definition: omapi.h:1267
CODE_ERROR_RANGE
#define CODE_ERROR_RANGE
verify
int verify(void)
Definition: verify.c:1060
Ticks
unsigned long long Ticks(OM_DATETIME timestamp, unsigned short fractional)
Definition: verify.c:173
OM_DOWNLOAD_COMPLETE
Data download completed successfully.
Definition: omapi.h:852
OM_VALUE_SCALE_ACCEL
Scaling: number of units for 1g: CWA=256, AX6=2048 (+/-16g), 4096 (+/-8g), 8192 (+/-4g),...
Definition: omapi.h:1266
OM_OK
#define OM_OK
Return code: Success.
Definition: omapi.h:1019
STUCK_COUNT
#define STUCK_COUNT
Definition: verify.c:83
CODE_WARNING_NANDID
#define CODE_WARNING_NANDID
OmCommand
int OmCommand(int deviceId, const char *command, char *buffer, size_t bufferSize, const char *expected, unsigned int timeoutMs, char **parseParts, int parseMax)
Issues a direct command over the CDC port for a particular device.
OM_READER_HEADER_PACKET
Internal structure of a binary file header block.
Definition: omapi.h:1292
CODE_ERROR_LIGHT
#define CODE_ERROR_LIGHT
OM_DATETIME_MONTH
#define OM_DATETIME_MONTH(dateTime)
Extract the month (1-12) from a packed date/time value.
Definition: omapi.h:1082
OM_READER_DATA_PACKET
Internal structure of a binary file data block.
Definition: omapi.h:1330
OM_ERASE_QUICKFORMAT
Device file-system is re-created and a new data file is created with the current metadata.
Definition: omapi.h:697
VERIFY_OPTION_ALL
#define VERIFY_OPTION_ALL
Definition: verify.c:78
OM_READER_HEADER_PACKET::loggingEndTime
OM_DATETIME loggingEndTime
@17 +4 Stop time for delayed logging
Definition: omapi.h:1301
OM_READER_DATA_PACKET::events
unsigned char events
@22 +1 Event flags since last packet, b0 = resume logging, b1 = single-tap event, b2 = double-tap eve...
Definition: omapi.h:1340
CODE_ERROR_STARTSTOP
#define CODE_ERROR_STARTSTOP
IGNORE_RECENT_RESTARTS
#define IGNORE_RECENT_RESTARTS
Definition: verify.c:88
CODE_ERROR_BREAKS
#define CODE_ERROR_BREAKS
OM_DATETIME_YEAR
#define OM_DATETIME_YEAR(dateTime)
Extract the year from a packed date/time value.
Definition: omapi.h:1081
download_t::nandType
int nandType
Definition: verify.c:99
OmReaderNextBlock
int OmReaderNextBlock(OmReaderHandle reader)
Reads the next block of data from the binary file.
OmReaderClose
void OmReaderClose(OmReaderHandle reader)
Closes the specified reader handle.
OM_FAILED
#define OM_FAILED(value)
Macro to check the specified return value for failure.
Definition: omapi.h:1033
OmReaderGetValue
int OmReaderGetValue(OmReaderHandle reader, OM_READER_VALUE_TYPE valueType)
Returns a specific value type from the buffer read by OmReaderNextBlock().
OmSetLed
int OmSetLed(int deviceId, OM_LED_STATE ledState)
Sets the specified device's LED colour.
CODE_ERROR_RESTARTS
#define CODE_ERROR_RESTARTS
OM_E_FAIL
#define OM_E_FAIL
Return code: Unspecified failure.
Definition: omapi.h:1020
CODE_ERROR_MASK
#define CODE_ERROR_MASK
CODE_WARNING_NANDHEALTH
#define CODE_WARNING_NANDHEALTH
OM_E_UNEXPECTED_RESPONSE
#define OM_E_UNEXPECTED_RESPONSE
Return code: Device response was not as expected.
Definition: omapi.h:1030
formattedtime
const char * formattedtime(unsigned long long milliseconds)
Definition: verify.c:188
OM_DATETIME_DAY
#define OM_DATETIME_DAY(dateTime)
Extract the day (1-31) from a packed date/time value.
Definition: omapi.h:1083
OM_E_NOT_IMPLEMENTED
#define OM_E_NOT_IMPLEMENTED
Return code: Requested functionality not implemented, either at the API level on this platform or the...
Definition: omapi.h:1026
OM_DATETIME_HOURS
#define OM_DATETIME_HOURS(dateTime)
Extract the hours (0-23) from a packed date/time value.
Definition: omapi.h:1084
CODE_WARNING_RATE
#define CODE_WARNING_RATE
CODE_WARNING_LIGHT
#define CODE_WARNING_LIGHT
LED_PROCESSING
#define LED_PROCESSING
Definition: verify.c:71
OmBeginDownloadingReference
int OmBeginDownloadingReference(int deviceId, int dataOffsetBlocks, int dataLengthBlocks, const char *destinationFile, void *reference)
Begin downloading the data from the current device, passing an additional reference.
AVERAGE_RANGE_MAX
#define AVERAGE_RANGE_MAX
Definition: verify.c:85
OM_DATETIME_ZERO
#define OM_DATETIME_ZERO
Special date/time value for "infinitely early".
Definition: omapi.h:1089
OM_VALUE_ACCEL_AXIS
Axis index for Accel-X (-Y and -Z follow), AX3=0, AX6(A)=0, AX6(GA/GAM)=3, not-present=-1.
Definition: omapi.h:1269
OM_DOWNLOAD_ERROR
Data download failed with an error (the value parameter to OmDownloadCallback indicates a diagnostic ...
Definition: omapi.h:850
OM_VERSION
#define OM_VERSION
A numeric code for current API version defined in this header file.
Definition: omapi.h:225
OM_VALUE_GYRO_AXIS
Axis index for Gyro-X (-Y and -Z follow), AX6(GA/GAM)=0, not-present=-1.
Definition: omapi.h:1270
download_t::memoryHealth
int memoryHealth
Definition: verify.c:96
OM_MEMORY_HEALTH_ERROR
#define OM_MEMORY_HEALTH_ERROR
Threshold at or below which the OmGetMemoryHealth() result should be treated as a failure.
Definition: omapi.h:431
CODE_WARNING_RESTARTS
#define CODE_WARNING_RESTARTS
CODE_ERROR_FILE
#define CODE_ERROR_FILE
OmReaderRawHeaderPacket
OM_READER_HEADER_PACKET * OmReaderRawHeaderPacket(OmReaderHandle reader)
Accesses the contents of a raw header packet.
verify_process
int verify_process(int id, const char *infile, download_t *download, int globalOptions)
Definition: verify.c:210
OM_DATETIME_SECONDS
#define OM_DATETIME_SECONDS(dateTime)
Extract the seconds (0-59) from a packed date/time value.
Definition: omapi.h:1086
download_t::hasGyro
bool hasGyro
Definition: verify.c:102
OM_READER_DATA_PACKET::timestamp
OM_DATETIME timestamp
@14 +4 Last reported RTC value, 0 = unknown
Definition: omapi.h:1337
OmGetDataRange
int OmGetDataRange(int deviceId, int *dataBlockSize, int *dataOffsetBlocks, int *dataNumBlocks, OM_DATETIME *startTime, OM_DATETIME *endTime)
Read the size, time-range, and internal chunking of the data buffer.
OmReaderTimestamp
OM_DATETIME OmReaderTimestamp(OmReaderHandle reader, int index, unsigned short *fractional)
Determines the timestamp of the specified sample in the buffer read by OmReaderNextBlock().
OM_DOWNLOAD_PROGRESS
Data download progress (the value parameter to OmDownloadCallback indicates progress percentage)
Definition: omapi.h:851
printf
#define printf(...)
Definition: download.c:57
CODE_WARNING_RANGE
#define CODE_WARNING_RANGE
OmShutdown
int OmShutdown(void)
Shuts down the Open Movement API.
OmSetDelays
int OmSetDelays(int deviceId, OM_DATETIME startTime, OM_DATETIME stopTime)
Sets the specified device's delayed activation start and stop times to use for a new recording sessio...
OM_DATETIME_BUFFER_SIZE
#define OM_DATETIME_BUFFER_SIZE
The size of a buffer to hold a string representation of an OM_DATETIME.
Definition: omapi.h:1093
OmGetDeviceSerial
int OmGetDeviceSerial(int deviceId, char *serialBuffer)
Return the full USB serial string identity for the specified device.
OmReaderOpen
OmReaderHandle OmReaderOpen(const char *binaryFilename)
Opens a binary data file for reading.
download_t::filename
char * filename
Definition: verify.c:101
CODE_ERROR_NANDID
#define CODE_ERROR_NANDID
OM_DATETIME_MINUTES
#define OM_DATETIME_MINUTES(dateTime)
Extract the minutes (0-59) from a packed date/time value.
Definition: omapi.h:1085
CODE_WARNING_BATT
#define CODE_WARNING_BATT
OM_MEMORY_HEALTH_WARNING
#define OM_MEMORY_HEALTH_WARNING
Threshold at or below which the OmGetMemoryHealth() result should be treated as a warning.
Definition: omapi.h:432
CODE_WARNING_EVENT
#define CODE_WARNING_EVENT
CODE_WARNING_FILE
#define CODE_WARNING_FILE
OM_READER_DATA_PACKET::sequenceId
unsigned int sequenceId
@10 +4 Sequence counter, each packet has a new number (reset if restarted)
Definition: omapi.h:1336
CODE_WARNING_BREAKS
#define CODE_WARNING_BREAKS
OmReaderHandle
void * OmReaderHandle
Handle to a reader object.
Definition: omapi.h:1127
OM_DATETIME_MAX_VALID
#define OM_DATETIME_MAX_VALID
The maximum valid date/time value.
Definition: omapi.h:1088
CODE_WARNING_MASK
#define CODE_WARNING_MASK
OM_DOWNLOAD_CANCELLED
Data download was cancelled cleanly.
Definition: omapi.h:853
OmSetDownloadCallback
int OmSetDownloadCallback(OmDownloadCallback downloadCallback, void *reference)
Sets the callback function that is called for download progress, completion, cancellation,...
LED_OK
#define LED_OK
Definition: verify.c:72
OM_DOWNLOAD_STATUS
OM_DOWNLOAD_STATUS
Download states used in the OmDownloadCallback handler.
Definition: omapi.h:847
Sleep
#define Sleep(millis)
Definition: deploy.c:49
OM_VALUE_AXES
Number of axes per sample.
Definition: omapi.h:1265
OmStartup
int OmStartup(int version)
Initializes the Open Movement API.
OmReaderBuffer
short * OmReaderBuffer(OmReaderHandle reader)
Obtains a pointer to the buffer of samples read, and unpacked, by OmReaderNextBlock().
CODE_WARNING_STUCK
#define CODE_WARNING_STUCK
download
int download(const char *outpath)
Definition: download.c:174
VERIFY_OPTION_NO_CHECK_STOP
#define VERIFY_OPTION_NO_CHECK_STOP
Definition: verify.c:79
AVERAGE_FACTOR
#define AVERAGE_FACTOR
Definition: verify.c:84
OmReaderRawDataPacket
OM_READER_DATA_PACKET * OmReaderRawDataPacket(OmReaderHandle reader)
Accesses the contents of a raw data packet.
OmEraseDataAndCommit
int OmEraseDataAndCommit(int deviceId, OM_ERASE_LEVEL eraseLevel)
Erases the specified device storage and commits the metadata and settings.
OM_MAX_PATH
#define OM_MAX_PATH
Macro for the maximum path length of a data filename.
Definition: omapi.h:840
OM_VALUE_LIGHT
Raw light sensor reading.
Definition: omapi.h:1252
LED_ERROR_COMMS
#define LED_ERROR_COMMS
Definition: verify.c:75
CODE_WARNING_STARTSTOP
#define CODE_WARNING_STARTSTOP
outfile
FILE * outfile
Definition: verify.c:154
CODE_ERROR_BATT
#define CODE_ERROR_BATT
HEADER
#define HEADER
TimeSerial
time_t TimeSerial(OM_DATETIME timestamp)
Definition: verify.c:157
download_t
Definition: verify.c:94
LED_FAILED
#define LED_FAILED
Definition: verify.c:74
OM_DEVICE_STATUS
OM_DEVICE_STATUS
Device states used in the OmDeviceCallback handler.
Definition: omapi.h:287