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 705864d0763fbeb1d6716c3ce8aa27726f0ec21f created 13 years 2 months ago. By Werner Almesberger, atrf-txrx: new option -r to select data rate | |
---|---|
1 | /* |
2 | * atrf-txrx/atrf-txrx.c - ben-wpan AT86RF230 TX/RX |
3 | * |
4 | * Written 2010-2011 by Werner Almesberger |
5 | * Copyright 2010-2011 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 <stdint.h> |
15 | #include <stdlib.h> |
16 | #include <stdio.h> |
17 | #include <unistd.h> |
18 | #include <string.h> |
19 | #include <math.h> |
20 | #include <signal.h> |
21 | #include <sys/wait.h> |
22 | #include <sys/time.h> |
23 | |
24 | #include "at86rf230.h" |
25 | #include "atrf.h" |
26 | #include "misctxrx.h" |
27 | |
28 | #include "pcap.h" |
29 | |
30 | |
31 | /* |
32 | * According to IEEE 802.15.4-2003 section E.2.6, channel 15 is the only |
33 | * channel that falls into the 802.11 guard bands in North America an Europe. |
34 | */ |
35 | |
36 | #define DEFAULT_CHANNEL 15 /* channel 15, 2425 MHz */ |
37 | |
38 | /* |
39 | * Transmit power, dBm. IEEE 802.15.4-2003 section E.3.1.3 specifies a transmit |
40 | * power of 0 dBm for IEEE 802.15.4. We assume an antenna gain of 3 dB or |
41 | * better. |
42 | */ |
43 | |
44 | #define DEFAULT_POWER -3.2 /* transmit power, dBm */ |
45 | |
46 | |
47 | struct ping { |
48 | uint32_t seq; /* sequence number from originator, > 0 */ |
49 | uint32_t ack; /* last sequence number received, 0 if none */ |
50 | uint8_t pad[117]; /* pad to 127 bytes */ |
51 | uint16_t crc; |
52 | } __attribute__((__packed__)); |
53 | |
54 | enum rx_res { |
55 | rx_exit, |
56 | rx_good, |
57 | rx_bad, |
58 | rx_timeout, |
59 | }; |
60 | |
61 | |
62 | static double tx_pwr_230[] = { |
63 | 3.0, 2.6, 2.1, 1.6, |
64 | 1.1, 0.5, -0.2, -1.2, |
65 | -2.2, -3.2, -4.2, -5.2, |
66 | -7.2, -9.2, -12.2, -17.2 |
67 | }; |
68 | |
69 | |
70 | static double tx_pwr_231[] = { |
71 | 3.0, 2.8, 2.3, 1.8, |
72 | 1.3, 0.7, 0.0, -1, |
73 | -2, -3, -4, -5, |
74 | -7, -9, -12, -17 |
75 | }; |
76 | |
77 | |
78 | static volatile int run = 1; |
79 | |
80 | |
81 | /* |
82 | * clkm: 0 disable CLKM |
83 | * >0 output 2^(clkm-1) MHz signal |
84 | */ |
85 | |
86 | static struct atrf_dsc *init_txrx(int trim, unsigned clkm) |
87 | { |
88 | struct atrf_dsc *dsc; |
89 | |
90 | dsc = atrf_open(); |
91 | if (!dsc) |
92 | exit(1); |
93 | |
94 | atrf_reset_rf(dsc); |
95 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TRX_OFF); |
96 | |
97 | #if 1 // def HAVE_USB /* @@@ yeah, ugly */ |
98 | atrf_reg_write(dsc, REG_XOSC_CTRL, |
99 | (XTAL_MODE_INT << XTAL_MODE_SHIFT) | trim); |
100 | #else |
101 | atrf_reg_write(dsc, REG_XOSC_CTRL, XTAL_MODE_EXT << XTAL_MODE_SHIFT); |
102 | #endif |
103 | |
104 | if (!clkm) |
105 | atrf_reg_write(dsc, REG_TRX_CTRL_0, 0); /* disable CLKM */ |
106 | else |
107 | atrf_reg_write(dsc, REG_TRX_CTRL_0, |
108 | (PAD_IO_8mA << PAD_IO_CLKM_SHIFT) | clkm); |
109 | |
110 | /* We want to see all interrupts, not only the ones we're expecting. */ |
111 | atrf_reg_write(dsc, REG_IRQ_MASK, 0xff); |
112 | |
113 | (void) atrf_reg_read(dsc, REG_IRQ_STATUS); |
114 | if (atrf_identify(dsc) == artf_at86rf231) |
115 | wait_for_interrupt(dsc, IRQ_CCA_ED_DONE, IRQ_CCA_ED_DONE, |
116 | 10, 50); /* according to table 7-1, 37 us max */ |
117 | |
118 | return dsc; |
119 | } |
120 | |
121 | |
122 | static void set_channel(struct atrf_dsc *dsc, int channel) |
123 | { |
124 | atrf_reg_write(dsc, REG_PHY_CC_CCA, (1 << CCA_MODE_SHIFT) | channel); |
125 | } |
126 | |
127 | |
128 | static void set_rate(struct atrf_dsc *dsc, uint8_t rate) |
129 | { |
130 | if (!rate) |
131 | return; |
132 | switch (atrf_identify(dsc)) { |
133 | case artf_at86rf230: |
134 | fprintf(stderr, "AT86RF230 only supports 250 kbps\n"); |
135 | break; |
136 | case artf_at86rf231: |
137 | atrf_reg_write(dsc, REG_TRX_CTRL_2, rate); |
138 | break; |
139 | default: |
140 | abort(); |
141 | } |
142 | } |
143 | |
144 | |
145 | static void set_power(struct atrf_dsc *dsc, double power, int crc) |
146 | { |
147 | const double *tx_pwr; |
148 | int n; |
149 | uint8_t tmp; |
150 | |
151 | switch (atrf_identify(dsc)) { |
152 | case artf_at86rf230: |
153 | tx_pwr = tx_pwr_230; |
154 | break; |
155 | case artf_at86rf231: |
156 | tx_pwr = tx_pwr_231; |
157 | break; |
158 | default: |
159 | abort(); |
160 | } |
161 | |
162 | for (n = 0; n != sizeof(tx_pwr_230)/sizeof(*tx_pwr_230)-1; n++) |
163 | if (tx_pwr[n] <= power) |
164 | break; |
165 | if (fabs(tx_pwr[n]-power) > 0.01) |
166 | fprintf(stderr, "TX power %.1f dBm\n", tx_pwr[n]); |
167 | |
168 | switch (atrf_identify(dsc)) { |
169 | case artf_at86rf230: |
170 | atrf_reg_write(dsc, REG_PHY_TX_PWR, |
171 | (crc ? TX_AUTO_CRC_ON_230 : 0) | n); |
172 | break; |
173 | case artf_at86rf231: |
174 | tmp = atrf_reg_read(dsc, REG_PHY_TX_PWR); |
175 | tmp = (tmp & ~TX_PWR_MASK) | n; |
176 | atrf_reg_write(dsc, REG_PHY_TX_PWR, tmp); |
177 | atrf_reg_write(dsc, REG_TRX_CTRL_1, |
178 | crc ? TX_AUTO_CRC_ON : 0); |
179 | break; |
180 | default: |
181 | abort(); |
182 | } |
183 | } |
184 | |
185 | |
186 | static void receive_message(struct atrf_dsc *dsc) |
187 | { |
188 | uint8_t buf[MAX_PSDU+1]; /* PSDU+LQI */ |
189 | int n, ok, i; |
190 | uint8_t ed, lqi; |
191 | |
192 | fprintf(stderr, "Ready.\n"); |
193 | wait_for_interrupt(dsc, IRQ_TRX_END, IRQ_TRX_END | IRQ_RX_START, |
194 | 10, 0); |
195 | if (!run) |
196 | return; |
197 | |
198 | n = atrf_buf_read(dsc, buf, sizeof(buf)); |
199 | if (n < 0) |
200 | exit(1); |
201 | if (n < 3) { |
202 | fprintf(stderr, "%d bytes received\n", n); |
203 | exit(1); |
204 | } |
205 | ed = atrf_reg_read(dsc, REG_PHY_ED_LEVEL); |
206 | ok = !!(atrf_reg_read(dsc, REG_PHY_RSSI) & RX_CRC_VALID); |
207 | lqi = buf[n-1]; |
208 | fprintf(stderr, "%d bytes payload, CRC %s, LQI %u, ED %d dBm\n", |
209 | n-3, ok ? "OK" : "BAD", lqi, -91+ed); |
210 | for (i = 0; i != n-3; i++) |
211 | putchar(buf[i] < ' ' || buf[i] > '~' ? '?' : buf[i]); |
212 | putchar('\n'); |
213 | } |
214 | |
215 | |
216 | static void write_pcap_hdr(FILE *file) |
217 | { |
218 | struct pcap_file_header hdr = { |
219 | .magic = PCAP_FILE_MAGIC, |
220 | .version_major = 2, |
221 | .version_minor = 4, |
222 | .thiszone = 0, |
223 | .sigfigs = 0, |
224 | .snaplen = MAX_PSDU, |
225 | .linktype = DLT_IEEE802_15_4 |
226 | }; |
227 | |
228 | if (fwrite(&hdr, sizeof(hdr), 1, file) != 1) { |
229 | perror("fwrite"); |
230 | exit(1); |
231 | } |
232 | } |
233 | |
234 | |
235 | static void write_pcap_rec(FILE *file, const struct timeval *tv, |
236 | const void *buf, int n) |
237 | { |
238 | struct pcap_pkthdr hdr = { |
239 | .ts_sec = tv->tv_sec, |
240 | .ts_usec = tv->tv_usec, |
241 | .caplen = n, |
242 | .len = n |
243 | }; |
244 | |
245 | if (fwrite(&hdr, sizeof(hdr), 1, file) != 1) { |
246 | perror("fwrite"); |
247 | exit(1); |
248 | } |
249 | if (fwrite(buf, n, 1, file) != 1) { |
250 | perror("fwrite"); |
251 | exit(1); |
252 | } |
253 | } |
254 | |
255 | |
256 | static void receive_pcap(struct atrf_dsc *dsc, const char *name) |
257 | { |
258 | FILE *file; |
259 | uint8_t buf[MAX_PSDU+1]; /* PSDU+LQI */ |
260 | struct timeval now; |
261 | int n; |
262 | int count = 0; |
263 | |
264 | file = fopen(name, "w"); |
265 | if (!file) { |
266 | perror(name); |
267 | exit(1); |
268 | } |
269 | write_pcap_hdr(file); |
270 | while (run) { |
271 | wait_for_interrupt(dsc, |
272 | IRQ_TRX_END, IRQ_TRX_END | IRQ_RX_START, |
273 | 10, 0); |
274 | if (!run) |
275 | break; |
276 | gettimeofday(&now, NULL); |
277 | n = atrf_buf_read(dsc, buf, sizeof(buf)); |
278 | if (n < 0) |
279 | exit(1); |
280 | if (n < 2) { |
281 | fprintf(stderr, "%d bytes received\n", n); |
282 | continue; |
283 | } |
284 | write_pcap_rec(file, &now, buf, n-1); |
285 | (void) write(2, ".", 1); |
286 | count++; |
287 | } |
288 | if (fclose(file) == EOF) { |
289 | perror(name); |
290 | exit(1); |
291 | } |
292 | fprintf(stderr, "%sreceived %d message%s\n", count ? "\n" : "", |
293 | count, count == 1 ? "" : "s"); |
294 | } |
295 | |
296 | |
297 | static void receive(struct atrf_dsc *dsc, const char *name) |
298 | { |
299 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_RX_ON); |
300 | /* |
301 | * 180 us, according to AVR2001 section 4.2. We time out after |
302 | * nominally 200 us. |
303 | */ |
304 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
305 | |
306 | if (name) |
307 | receive_pcap(dsc, name); |
308 | else |
309 | receive_message(dsc); |
310 | } |
311 | |
312 | |
313 | static void transmit(struct atrf_dsc *dsc, const char *msg, int times) |
314 | { |
315 | uint8_t buf[MAX_PSDU]; |
316 | |
317 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
318 | /* |
319 | * 180 us, according to AVR2001 section 4.3. We time out after |
320 | * nominally 200 us. |
321 | */ |
322 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
323 | |
324 | /* |
325 | * We need to copy the message to append the CRC placeholders. |
326 | */ |
327 | strcpy((void *) buf, msg); |
328 | atrf_buf_write(dsc, buf, strlen(msg)+2); |
329 | |
330 | while (run && times--) { |
331 | /* @@@ should wait for clear channel */ |
332 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
333 | |
334 | /* wait up to 10 ms (nominally) */ |
335 | wait_for_interrupt(dsc, IRQ_TRX_END, |
336 | IRQ_TRX_END | IRQ_PLL_LOCK, 10, 1000); |
337 | } |
338 | } |
339 | |
340 | |
341 | static void enter_test_mode_230(struct atrf_dsc *dsc, uint8_t cont_tx) |
342 | { |
343 | atrf_buf_write(dsc, "", 1); |
344 | atrf_reg_write(dsc, REG_CONT_TX_0, CONT_TX_MAGIC); |
345 | atrf_reg_write(dsc, REG_CONT_TX_1, cont_tx); |
346 | |
347 | if (!atrf_test_mode(dsc)) { |
348 | atrf_reset_rf(dsc); |
349 | fprintf(stderr, "device does not support test mode\n"); |
350 | exit(1); |
351 | } |
352 | |
353 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
354 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
355 | |
356 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
357 | } |
358 | |
359 | |
360 | static void enter_test_mode_231(struct atrf_dsc *dsc, uint8_t cont_tx) |
361 | { |
362 | uint8_t buf[127]; |
363 | uint8_t status; |
364 | |
365 | switch (cont_tx) { |
366 | case CONT_TX_M2M: |
367 | fprintf(stderr, |
368 | "-2 MHz mode is not supported by the AT86RF231\n"); |
369 | atrf_close(dsc); |
370 | exit(1); |
371 | case CONT_TX_M500K: |
372 | memset(buf, 0, sizeof(buf)); |
373 | break; |
374 | case CONT_TX_P500K: |
375 | memset(buf, 0xff, sizeof(buf)); |
376 | break; |
377 | default: |
378 | abort(); |
379 | } |
380 | |
381 | atrf_reg_write(dsc, REG_IRQ_MASK, IRQ_PLL_LOCK); /* 2 */ |
382 | atrf_reg_write(dsc, REG_TRX_CTRL_1, 0); /* 3 */ |
383 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_FORCE_TRX_OFF); /* 4 */ |
384 | /* deleted step 5 - we don't need to enable CLKM */ |
385 | |
386 | status = atrf_reg_read(dsc, REG_TRX_STATUS) & TRX_STATUS_MASK; /* 8 */ |
387 | if (status != TRX_STATUS_TRX_OFF) { |
388 | fprintf(stderr, "expected status 0x%02x, got 0x%02x\n", |
389 | TRX_STATUS_TRX_OFF, status); |
390 | exit(1); |
391 | } |
392 | |
393 | atrf_reg_write(dsc, REG_CONT_TX_0, CONT_TX_MAGIC); /* 9 */ |
394 | atrf_reg_write(dsc, REG_TRX_CTRL_2, OQPSK_DATA_RATE_2000); /*10 */ |
395 | atrf_reg_write(dsc, REG_RX_CTRL, 0xa7); /*11 */ |
396 | |
397 | atrf_buf_write(dsc, buf, sizeof(buf)); /*12 */ |
398 | |
399 | atrf_reg_write(dsc, REG_PART_NUM, 0x54); /*13 */ |
400 | atrf_reg_write(dsc, REG_PART_NUM, 0x46); /*14 */ |
401 | |
402 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); /*15 */ |
403 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 0); /*16 */ |
404 | |
405 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); /*17 */ |
406 | } |
407 | |
408 | |
409 | static void transmit_pattern(struct atrf_dsc *dsc, double pause_s, int times) |
410 | { |
411 | uint8_t buf[MAX_PSDU]; |
412 | uint8_t n = 0; |
413 | int us = fmod(pause_s, 1)*1000000; |
414 | |
415 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
416 | /* |
417 | * 180 us, according to AVR2001 section 4.3. We time out after |
418 | * nominally 200 us. |
419 | */ |
420 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
421 | |
422 | while (run) { |
423 | memset(buf, n, sizeof(buf)); |
424 | atrf_buf_write(dsc, buf, sizeof(buf)); |
425 | |
426 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
427 | |
428 | /* wait up to 10 ms (nominally) */ |
429 | wait_for_interrupt(dsc, IRQ_TRX_END, |
430 | IRQ_TRX_END | IRQ_PLL_LOCK, 10, 1000); |
431 | |
432 | if (pause_s >= 1) |
433 | sleep(pause_s); |
434 | if (us) |
435 | usleep(us); |
436 | |
437 | if (times && !--times) |
438 | break; |
439 | n++; |
440 | } |
441 | } |
442 | |
443 | |
444 | static void ping_tx(struct atrf_dsc *dsc, const struct ping *pck) |
445 | { |
446 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
447 | /* |
448 | * 180 us, according to AVR2001 section 4.3. We time out after |
449 | * nominally 200 us. |
450 | */ |
451 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
452 | |
453 | atrf_buf_write(dsc, pck, sizeof(*pck)); |
454 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
455 | |
456 | /* wait up to 10 ms (nominally) */ |
457 | wait_for_interrupt(dsc, IRQ_TRX_END, |
458 | IRQ_TRX_END | IRQ_PLL_LOCK, 10, 1000); |
459 | } |
460 | |
461 | |
462 | static enum rx_res ping_rx(struct atrf_dsc *dsc, struct ping *pck, int wait_ds) |
463 | { |
464 | uint8_t irq; |
465 | int n; |
466 | |
467 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_RX_ON); |
468 | irq = wait_for_interrupt(dsc, IRQ_TRX_END, |
469 | IRQ_TRX_END | IRQ_RX_START | IRQ_PLL_LOCK, |
470 | 100000, wait_ds); |
471 | if (!run) |
472 | return rx_exit; |
473 | if (!irq) |
474 | return rx_timeout; |
475 | |
476 | n = atrf_buf_read(dsc, pck, sizeof(*pck)); |
477 | if (n < 0) |
478 | exit(1); |
479 | if (n != sizeof(*pck)) { |
480 | fprintf(stderr, "%d bytes received\n", n); |
481 | return rx_bad; |
482 | } |
483 | return atrf_reg_read(dsc, REG_PHY_RSSI) & RX_CRC_VALID ? |
484 | rx_good : rx_bad; |
485 | } |
486 | |
487 | |
488 | static void ping(struct atrf_dsc *dsc, double max_wait_s, int master) |
489 | { |
490 | static int first = 1; |
491 | struct ping tx_pck = { |
492 | .seq = 0, |
493 | .ack = 0, |
494 | }; |
495 | struct ping rx_pck; |
496 | enum rx_res res; |
497 | |
498 | while (run) { |
499 | tx_pck.seq++; |
500 | if (master || !first) { |
501 | ping_tx(dsc, &tx_pck); |
502 | if (!run) |
503 | break; |
504 | } |
505 | first = 0; |
506 | res = ping_rx(dsc, &rx_pck, master ? max_wait_s*10 : 0); |
507 | switch (res) { |
508 | case rx_good: |
509 | tx_pck.ack = rx_pck.seq; |
510 | if (tx_pck.seq == rx_pck.ack) |
511 | write(2, ".", 1); |
512 | else |
513 | write(2, "*", 1); |
514 | break; |
515 | case rx_bad: |
516 | write(2, "-", 1); |
517 | break; |
518 | case rx_timeout: |
519 | write(2, "+", 1); |
520 | break; |
521 | case rx_exit: |
522 | return; |
523 | default: |
524 | abort(); |
525 | } |
526 | } |
527 | } |
528 | |
529 | |
530 | static int test_mode(struct atrf_dsc *dsc, uint8_t cont_tx, const char *cmd) |
531 | { |
532 | int status = 0; |
533 | |
534 | switch (atrf_identify(dsc)) { |
535 | case artf_at86rf230: |
536 | enter_test_mode_230(dsc, cont_tx); |
537 | break; |
538 | case artf_at86rf231: |
539 | enter_test_mode_231(dsc, cont_tx); |
540 | break; |
541 | default: |
542 | abort(); |
543 | } |
544 | |
545 | if (cmd) |
546 | status = system(cmd); |
547 | else { |
548 | while (run) |
549 | sleep(1); |
550 | } |
551 | |
552 | if (atrf_identify(dsc) == artf_at86rf231) |
553 | atrf_reg_write(dsc, REG_PART_NUM, 0); |
554 | |
555 | atrf_reset_rf(dsc); |
556 | |
557 | return status; |
558 | } |
559 | |
560 | |
561 | static void die(int sig) |
562 | { |
563 | run = 0; |
564 | } |
565 | |
566 | |
567 | static void usage(const char *name) |
568 | { |
569 | fprintf(stderr, |
570 | "usage: %s [common_options] [message [repetitions]]\n" |
571 | " %s [common_options] -E pause_s [repetitions]\n" |
572 | " %s [common_options] -P [max_wait_s]\n" |
573 | " %s [common_options] -T offset [command]\n\n" |
574 | " text message mode:\n" |
575 | " message message string to send (if absent, receive)\n" |
576 | " repetitions number of times the message is sent (default 1)\n\n" |
577 | " PER test mode (transmit only):\n" |
578 | " -E pause_s seconds to pause between frames (floating-point)\n" |
579 | " repetitions number of messages to send (default: infinite)\n\n" |
580 | " Ping-pong mode:\n" |
581 | " -P exchange packets between two stations\n" |
582 | " max_wait_s generate a new packet if no response is received (master)\n\n" |
583 | " constant wave test mode (transmit only):\n" |
584 | " -T offset test mode. offset is the frequency offset of the constant\n" |
585 | " wave in MHz: -2, -0.5, or +0.5\n" |
586 | " command shell command to run while transmitting (default: wait for\n" |
587 | " SIGINT instead)\n\n" |
588 | " common options: [-c channel|-f freq] [-C mhz] [-o file] [-p power]\n" |
589 | " [-r rate] [-t trim]\n" |
590 | " -c channel channel number, 11 to 26 (default %d)\n" |
591 | " -C mhz output clock at 1, 2, 4, 8, or 16 MHz (default: off)\n" |
592 | " -f freq frequency in MHz, 2405 to 2480 (default %d)\n" |
593 | " -o file write received data to a file in pcap format\n" |
594 | " -p power transmit power, -17.2 to 3.0 dBm (default %.1f)\n" |
595 | " -r rate data rate, 250k, 500k, 1M, or 2M (default: 250k)\n" |
596 | " -t trim trim capacitor, 0 to 15 (default 0)\n" |
597 | , name, name, name, name, |
598 | DEFAULT_CHANNEL, 2405+5*(DEFAULT_CHANNEL-11), DEFAULT_POWER); |
599 | exit(1); |
600 | } |
601 | |
602 | |
603 | int main(int argc, char *const *argv) |
604 | { |
605 | enum { |
606 | mode_msg, |
607 | mode_per, |
608 | mode_ping, |
609 | mode_cont_tx, |
610 | } mode = mode_msg; |
611 | int channel = DEFAULT_CHANNEL; |
612 | double power = DEFAULT_POWER; |
613 | uint8_t rate = OQPSK_DATA_RATE_250; |
614 | int trim = 0, times = 1; |
615 | uint8_t cont_tx = 0; |
616 | double pause_s = 0; |
617 | char *end; |
618 | int c, freq; |
619 | unsigned tmp, clkm = 0; |
620 | int status = 0; |
621 | const char *pcap_file = NULL; |
622 | struct atrf_dsc *dsc; |
623 | |
624 | while ((c = getopt(argc, argv, "c:C:f:o:p:r:E:Pt:T:")) != EOF) |
625 | switch (c) { |
626 | case 'c': |
627 | channel = strtoul(optarg, &end, 0); |
628 | if (*end) |
629 | usage(*argv); |
630 | if (channel < 11 || channel > 26) |
631 | usage(*argv); |
632 | break; |
633 | case 'f': |
634 | freq = strtoul(optarg, &end, 0); |
635 | if (*end) |
636 | usage(*argv); |
637 | if (freq % 5) |
638 | usage(*argv); |
639 | channel = (freq-2405)/5+11; |
640 | if (channel < 11 || channel > 26) |
641 | usage(*argv); |
642 | break; |
643 | case 'o': |
644 | pcap_file = optarg; |
645 | break; |
646 | case 'p': |
647 | power = strtod(optarg, &end); |
648 | if (*end) |
649 | usage(*argv); |
650 | break; |
651 | case 'r': |
652 | if (!strcmp(optarg, "250k")) |
653 | rate = OQPSK_DATA_RATE_250; |
654 | else if (!strcmp(optarg, "500k")) |
655 | rate = OQPSK_DATA_RATE_500; |
656 | else if (!strcmp(optarg, "1M")) |
657 | rate = OQPSK_DATA_RATE_1000; |
658 | else if (!strcmp(optarg, "2M")) |
659 | rate = OQPSK_DATA_RATE_2000; |
660 | else |
661 | usage(*argv); |
662 | break; |
663 | case 't': |
664 | trim = strtoul(optarg, &end, 0); |
665 | if (*end) |
666 | usage(*argv); |
667 | if (trim > 15) |
668 | usage(*argv); |
669 | break; |
670 | case 'C': |
671 | tmp = strtol(optarg, &end, 0); |
672 | if (*end) |
673 | usage(*argv); |
674 | if (!tmp) |
675 | usage(*argv); |
676 | for (clkm = 1; !(tmp & 1); tmp >>= 1) |
677 | clkm++; |
678 | if (tmp != 1 || clkm > 5) |
679 | usage(*argv); |
680 | break; |
681 | case 'E': |
682 | mode = mode_per; |
683 | pause_s = strtof(optarg, &end); |
684 | if (*end) |
685 | usage(*argv); |
686 | break; |
687 | case 'P': |
688 | mode = mode_ping; |
689 | break; |
690 | case 'T': |
691 | mode = mode_cont_tx; |
692 | if (!strcmp(optarg, "-2")) |
693 | cont_tx = CONT_TX_M2M; |
694 | else if (!strcmp(optarg, "-0.5")) |
695 | cont_tx = CONT_TX_M500K; |
696 | else if (!strcmp(optarg, "+0.5")) |
697 | cont_tx = CONT_TX_P500K; |
698 | else |
699 | usage(*argv); |
700 | break; |
701 | default: |
702 | usage(*argv); |
703 | } |
704 | |
705 | signal(SIGINT, die); |
706 | |
707 | switch (argc-optind) { |
708 | case 0: |
709 | dsc = init_txrx(trim, clkm); |
710 | set_channel(dsc, channel); |
711 | set_rate(dsc, rate); |
712 | switch (mode) { |
713 | case mode_msg: |
714 | receive(dsc, pcap_file); |
715 | break; |
716 | case mode_per: |
717 | set_power(dsc, power, 0); |
718 | transmit_pattern(dsc, pause_s, 0); |
719 | break; |
720 | case mode_ping: |
721 | set_power(dsc, power, 1); |
722 | ping(dsc, pause_s, 0); |
723 | break; |
724 | case mode_cont_tx: |
725 | set_power(dsc, power, 0); |
726 | status = test_mode(dsc, cont_tx, NULL); |
727 | break; |
728 | default: |
729 | abort(); |
730 | } |
731 | break; |
732 | case 2: |
733 | switch (mode) { |
734 | case mode_msg: |
735 | break; |
736 | case mode_per: |
737 | case mode_ping: |
738 | /* fall through */ |
739 | case mode_cont_tx: |
740 | usage(*argv); |
741 | default: |
742 | abort(); |
743 | } |
744 | times = strtoul(argv[optind+1], &end, 0); |
745 | if (*end) |
746 | usage(*argv); |
747 | /* fall through */ |
748 | case 1: |
749 | dsc = init_txrx(trim, clkm); |
750 | set_channel(dsc, channel); |
751 | set_rate(dsc, rate); |
752 | switch (mode) { |
753 | case mode_msg: |
754 | set_power(dsc, power, 1); |
755 | transmit(dsc, argv[optind], times); |
756 | break; |
757 | case mode_per: |
758 | times = strtoul(argv[optind+1], &end, 0); |
759 | if (*end) |
760 | usage(*argv); |
761 | set_power(dsc, power, 0); |
762 | transmit_pattern(dsc, pause_s, times); |
763 | break; |
764 | case mode_ping: |
765 | pause_s = strtof(argv[optind], &end); |
766 | if (*end) |
767 | usage(*argv); |
768 | set_power(dsc, power, 1); |
769 | ping(dsc, pause_s, 1); |
770 | break; |
771 | case mode_cont_tx: |
772 | set_power(dsc, power, 0); |
773 | status = test_mode(dsc, cont_tx, argv[optind]); |
774 | break; |
775 | default: |
776 | abort(); |
777 | |
778 | } |
779 | break; |
780 | default: |
781 | usage(*argv); |
782 | } |
783 | |
784 | atrf_close(dsc); |
785 | |
786 | if (status) { |
787 | if (WIFEXITED(status)) |
788 | return WEXITSTATUS(status); |
789 | if (WIFSIGNALED(status)) |
790 | raise(WTERMSIG(status)); |
791 | fprintf(stderr, "unexpected exit status %d\n", status); |
792 | abort(); |
793 | } |
794 | return 0; |
795 | } |
796 |