IEEE 802.15.4 subsystem
Sign in or create your account | Project List | Help
IEEE 802.15.4 subsystem Git Source Tree
Root/
Source at commit df1706fe747cecffd2b417e654c42891d9367682 created 13 years 4 months ago. By Werner Almesberger, Added burst counter mode to cntr tool, plus assorted small improvements. | |
---|---|
1 | /* |
2 | * cntr/cntr.c - CNTR control tool |
3 | * |
4 | * Written 2010 by Werner Almesberger |
5 | * Copyright 2010 Werner Almesberger |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | */ |
12 | |
13 | |
14 | #include <stdlib.h> |
15 | #include <stdio.h> |
16 | #include <signal.h> |
17 | #include <usb.h> |
18 | #include <sys/time.h> |
19 | |
20 | #include "f32xbase/usb.h" |
21 | #include "cntr/ep0.h" |
22 | #include "cntr/usb-ids.h" |
23 | |
24 | |
25 | #define FROM_DEV CNTR_FROM_DEV(0) |
26 | #define TO_DEV CNTR_TO_DEV(0) |
27 | |
28 | #define DEFAULT_CLOCK_DEV_S 0.1 /* 100 ms, typ. NTP over WLAN dev. */ |
29 | #define BUF_SIZE 256 |
30 | |
31 | |
32 | static int debug = 0; |
33 | static int verbose = 0; |
34 | |
35 | |
36 | /* ----- CRC, shared with firmware ----------------------------------------- */ |
37 | |
38 | |
39 | /* crc32() */ |
40 | |
41 | #include "cntr/crc32.c" |
42 | |
43 | |
44 | /* ----- reset ------------------------------------------------------------- */ |
45 | |
46 | |
47 | static void reset_cntr(usb_dev_handle *dev) |
48 | { |
49 | int res; |
50 | |
51 | res = usb_control_msg(dev, TO_DEV, CNTR_RESET, 0, 0, NULL, 0, 1000); |
52 | if (res < 0) { |
53 | fprintf(stderr, "CNTR_RESET: %d\n", res); |
54 | exit(1); |
55 | } |
56 | } |
57 | |
58 | |
59 | /* ----- identify ---------------------------------------------------------- */ |
60 | |
61 | |
62 | static void identify_cntr(usb_dev_handle *dev) |
63 | { |
64 | const struct usb_device *device = usb_device(dev); |
65 | uint8_t ids[3]; |
66 | char buf[BUF_SIZE+1]; /* +1 for terminating \0 */ |
67 | int res; |
68 | |
69 | printf("%04x:%04x ", |
70 | device->descriptor.idVendor, device->descriptor.idProduct); |
71 | |
72 | res = usb_control_msg(dev, FROM_DEV, CNTR_ID, 0, 0, |
73 | (char *) ids, sizeof(ids), 1000); |
74 | if (res < 0) { |
75 | fprintf(stderr, "CNTR_ID: %s\n", usb_strerror()); |
76 | exit(1); |
77 | } |
78 | |
79 | printf("protocol %u.%u hw %u\n", ids[0], ids[1], ids[2]); |
80 | |
81 | res = usb_control_msg(dev, FROM_DEV, CNTR_BUILD, 0, 0, |
82 | buf, sizeof(buf), 1000); |
83 | if (res < 0) { |
84 | fprintf(stderr, "CNTR_BUILD: %s\n", usb_strerror()); |
85 | exit(1); |
86 | } |
87 | buf[res] = 0; |
88 | printf("%10s%s\n", "", buf); |
89 | } |
90 | |
91 | |
92 | /* ---- packet reception --------------------------------------------------- */ |
93 | |
94 | |
95 | struct sample { |
96 | double t0, t1; |
97 | uint64_t cntr; |
98 | }; |
99 | |
100 | |
101 | static unsigned packets = 0, crc_errors = 0, inv_errors = 0; |
102 | |
103 | |
104 | static int get_sample(usb_dev_handle *dev, struct sample *s) |
105 | { |
106 | static uint32_t last = 0, high = 0; |
107 | struct timeval t0, t1; |
108 | int res, bad; |
109 | uint8_t buf[12]; |
110 | uint32_t cntr, inv, crc, expect; |
111 | |
112 | gettimeofday(&t0, NULL); |
113 | res = usb_control_msg(dev, FROM_DEV, CNTR_READ, 0, 0, |
114 | (char *) buf, sizeof(buf), 1000); |
115 | gettimeofday(&t1, NULL); |
116 | if (res < 0) { |
117 | fprintf(stderr, "CNTR_READ: %s\n", usb_strerror()); |
118 | exit(1); |
119 | } |
120 | packets++; |
121 | cntr = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); |
122 | crc = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); |
123 | inv = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24); |
124 | expect = crc32(cntr, ~0); |
125 | bad = 0; |
126 | if (crc != expect) { |
127 | if (verbose) |
128 | fprintf(stderr, "\nCRC error (count 0x%08x->0x%08x " |
129 | "CRC 0x%08x/0x%08x)\n", |
130 | (unsigned) last, (unsigned) cntr, (unsigned) crc, |
131 | (unsigned) expect); |
132 | bad = 1; |
133 | crc_errors++; |
134 | } |
135 | if (cntr != (inv ^ 0xffffffff)) { |
136 | if (verbose) |
137 | fprintf(stderr, |
138 | "\ninverted counter error (0x%08x->0x%08x, " |
139 | "inv 0x%08x)\n", |
140 | (unsigned) last, (unsigned) cntr, (unsigned) inv); |
141 | bad = 1; |
142 | inv_errors++; |
143 | } |
144 | if (bad) |
145 | return 0; |
146 | if (last > cntr) |
147 | high++; |
148 | last = cntr; |
149 | s->t0 = t0.tv_sec+t0.tv_usec/1000000.0; |
150 | s->t1 = t1.tv_sec+t1.tv_usec/1000000.0; |
151 | s->cntr = (uint64_t) high << 32 | cntr; |
152 | if (debug) |
153 | printf("0x%llx 0x%lx\n", |
154 | (unsigned long long ) s->cntr, (unsigned long) cntr); |
155 | return 1; |
156 | } |
157 | |
158 | |
159 | /* ---- SIGINT (Ctrl-C) ---------------------------------------------------- */ |
160 | |
161 | |
162 | static volatile int stop = 0; |
163 | |
164 | |
165 | static void set_stop(int sig) |
166 | { |
167 | stop = 1; |
168 | } |
169 | |
170 | |
171 | static void arm_stop(void) |
172 | { |
173 | signal(SIGINT, set_stop); |
174 | } |
175 | |
176 | |
177 | /* ---- burst counter ------------------------------------------------------ */ |
178 | |
179 | |
180 | static void count_bursts(usb_dev_handle *dev, double timeout) |
181 | { |
182 | struct sample start, stable, now; |
183 | |
184 | arm_stop(); |
185 | |
186 | while (!get_sample(dev, &start)); |
187 | stable = start; |
188 | while (!stop) { |
189 | while (!get_sample(dev, &now)) |
190 | if (stop) |
191 | break; |
192 | if (stable.cntr != now.cntr) { |
193 | stable = now; |
194 | continue; |
195 | } |
196 | if (now.t0-stable.t1 < timeout) |
197 | continue; |
198 | if (now.cntr == start.cntr) |
199 | continue; |
200 | printf("%llu\n", (unsigned long long) now.cntr-start.cntr); |
201 | start = now; |
202 | } |
203 | } |
204 | |
205 | |
206 | /* ----- measurements ------------------------------------------------------ */ |
207 | |
208 | |
209 | static void measure(usb_dev_handle *dev, double clock_dev_s, double error_goal) |
210 | { |
211 | struct sample start, now; |
212 | uint64_t dc; |
213 | double dt, f, error; |
214 | char *f_exp, *error_exp; |
215 | int i; |
216 | |
217 | arm_stop(); |
218 | |
219 | /* |
220 | * The round-trip time for getting the first sample is one of the |
221 | * error terms. The smaller we can make it, the better. Thus, we try a |
222 | * few times to improve our first result. |
223 | */ |
224 | while (!get_sample(dev, &start)); |
225 | for (i = 0; i != 10; i++) { |
226 | while (!get_sample(dev, &now)); |
227 | if (now.t1-now.t0 < start.t1-start.t0) { |
228 | if (debug) |
229 | fprintf(stderr, "improve %g -> %g\n", |
230 | start.t1-start.t0, now.t1-now.t0); |
231 | start = now; |
232 | } |
233 | } |
234 | while (!stop) { |
235 | usleep(100000); |
236 | while (!get_sample(dev, &now)) |
237 | if (stop) |
238 | break; |
239 | dc = now.cntr-start.cntr; |
240 | dt = (now.t0+now.t1)/2.0-(start.t0+start.t1)/2.0; |
241 | f = dc/dt; |
242 | if (f > 1000000.0) { |
243 | f /= 1000000.0; |
244 | f_exp = "M"; |
245 | } else if (f > 1000.0) { |
246 | f /= 1000.0; |
247 | f_exp = "k"; |
248 | } else { |
249 | f_exp = ""; |
250 | } |
251 | if (dc) |
252 | error = 1.0/dc; /* one count */ |
253 | else |
254 | error = 0; |
255 | error += (start.t1-start.t0)/dt/2.0; /* start sample read */ |
256 | error += (now.t1-now.t0)/dt/2.0; /* last sample read */ |
257 | error += clock_dev_s/dt; /* system clock dev. */ |
258 | if (error >= 1) { |
259 | printf("\r(wait) "); |
260 | fflush(stdout); |
261 | continue; |
262 | } |
263 | if (dc && error <= error_goal) |
264 | stop = 1; |
265 | |
266 | error_exp = "%"; |
267 | error *= 100.0; |
268 | if (error < 0.1) { |
269 | error_exp = " ppm"; |
270 | error *= 10000.0; |
271 | if (error < 1.0) { |
272 | error_exp = " ppb"; |
273 | error *= 1000.0; |
274 | } |
275 | } |
276 | |
277 | printf("\r%6.1f %1.9f %sHz %3.3f%s ", |
278 | dt, f, f_exp, error, error_exp); |
279 | fflush(stdout); |
280 | } |
281 | printf( |
282 | "\n%llu counts, %u packets, %u CRC error%s, %u invert error%s\n", |
283 | (unsigned long long) (now.cntr-start.cntr), |
284 | packets, crc_errors, crc_errors == 1 ? "" : "s", |
285 | inv_errors, inv_errors == 1 ? "" : "s"); |
286 | } |
287 | |
288 | |
289 | /* ----- command-line parsing ---------------------------------------------- */ |
290 | |
291 | |
292 | static void usage(const char *name) |
293 | { |
294 | fprintf(stderr, |
295 | "usage: %s [-c clock_dev] [-d] [-v] [accuracy_ppm]\n" |
296 | "%6s %s -b [-d] [-v] [timeout_s]\n" |
297 | "%6s %s -i\n" |
298 | "%6s %s -r\n\n" |
299 | " accuracy_ppm stop when specified accuracy is reached (default: never\n" |
300 | " stop)\n" |
301 | " timeout_s silence period between bursts, in seconds (default: 1s )\n" |
302 | " -b count bursts separated by silence periods\n" |
303 | " -c clock_dev maximum deviation of the system clock, in seconds\n" |
304 | " (default: %g s)\n" |
305 | " -d debug mode. Print counter values.\n" |
306 | " -i identify the CNTR board\n" |
307 | " -r reset the CNTR board\n" |
308 | " -v verbose reporting of communication errors\n" |
309 | , name, "", name, "", name, "", name, DEFAULT_CLOCK_DEV_S); |
310 | exit(1); |
311 | } |
312 | |
313 | |
314 | int main(int argc, char *const *argv) |
315 | { |
316 | usb_dev_handle *dev; |
317 | int c, burst = 0, identify = 0, reset = 0; |
318 | double clock_dev_s = DEFAULT_CLOCK_DEV_S; |
319 | double error_goal = 0; |
320 | char *end; |
321 | |
322 | while ((c = getopt(argc, argv, "bc:dir")) != EOF) |
323 | switch (c) { |
324 | case 'b': |
325 | burst = 1; |
326 | break; |
327 | case 'c': |
328 | clock_dev_s = strtod(argv[optind], &end); |
329 | if (*end) |
330 | usage(*argv); |
331 | break; |
332 | case 'd': |
333 | debug = 1; |
334 | break; |
335 | case 'i': |
336 | identify = 1; |
337 | break; |
338 | case 'r': |
339 | reset = 1; |
340 | break; |
341 | default: |
342 | usage(*argv); |
343 | } |
344 | if (burst+identify+reset > 1) |
345 | usage(*argv); |
346 | |
347 | switch (argc-optind) { |
348 | case 0: |
349 | break; |
350 | case 1: |
351 | if (identify || reset) |
352 | usage(*argv); |
353 | error_goal = strtod(argv[optind], &end)/1000000.0; |
354 | if (*end) |
355 | usage(*argv); |
356 | break; |
357 | default: |
358 | usage(*argv); |
359 | } |
360 | |
361 | dev = open_usb(USB_VENDOR, USB_PRODUCT); |
362 | if (!dev) { |
363 | fprintf(stderr, ":-(\n"); |
364 | return 1; |
365 | } |
366 | |
367 | if (identify) { |
368 | identify_cntr(dev); |
369 | return 0; |
370 | } |
371 | |
372 | if (reset) { |
373 | reset_cntr(dev); |
374 | return 0; |
375 | } |
376 | |
377 | if (burst) { |
378 | count_bursts(dev, error_goal ? error_goal : 1); |
379 | return 0; |
380 | } |
381 | |
382 | measure(dev, clock_dev_s, error_goal); |
383 | |
384 | return 0; |
385 | } |
386 |