]> git.zerfleddert.de Git - proxmark3-svn/blame_incremental - winsrc/command.cpp
Add command and code for bidirectional LF emulation of Hitag2. Should be extended...
[proxmark3-svn] / winsrc / command.cpp
... / ...
CommitLineData
1//-----------------------------------------------------------------------------\r
2// The actual command interpeter for what the user types at the command line.\r
3// Jonathan Westhues, Sept 2005\r
4// Edits by Gerhard de Koning Gans, Sep 2007 (##)\r
5//-----------------------------------------------------------------------------\r
6#include <windows.h>\r
7#include <stdlib.h>\r
8#include <string.h>\r
9#include <stdio.h>\r
10#include <limits.h>\r
11#include <math.h>\r
12\r
13#include "prox.h"\r
14#include "../common/iso14443_crc.c"\r
15#include "../common/crc16.c"\r
16\r
17#define arraylen(x) (sizeof(x)/sizeof((x)[0]))\r
18#define BIT(x) GraphBuffer[x * clock]\r
19#define BITS (GraphTraceLen / clock)\r
20\r
21int go = 0;\r
22static int CmdHisamplest(char *str, int nrlow);\r
23\r
24static void GetFromBigBuf(BYTE *dest, int bytes)\r
25{\r
26 int n = bytes/4;\r
27\r
28 if(n % 48 != 0) {\r
29 PrintToScrollback("bad len in GetFromBigBuf");\r
30 return;\r
31 }\r
32\r
33 int i;\r
34 for(i = 0; i < n; i += 12) {\r
35 UsbCommand c;\r
36 c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K;\r
37 c.ext1 = i;\r
38 SendCommand(&c, FALSE);\r
39 ReceiveCommand(&c);\r
40 if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) {\r
41 PrintToScrollback("bad resp");\r
42 return;\r
43 }\r
44\r
45 memcpy(dest+(i*4), c.d.asBytes, 48);\r
46 }\r
47}\r
48\r
49static void CmdReset(char *str)\r
50{\r
51 UsbCommand c;\r
52 c.cmd = CMD_HARDWARE_RESET;\r
53 SendCommand(&c, FALSE);\r
54}\r
55\r
56static void CmdBuffClear(char *str)\r
57{\r
58 UsbCommand c;\r
59 c.cmd = CMD_BUFF_CLEAR;\r
60 SendCommand(&c, FALSE);\r
61 CmdClearGraph(TRUE);\r
62}\r
63\r
64static void CmdQuit(char *str)\r
65{\r
66 exit(0);\r
67}\r
68\r
69static void CmdHIDdemodFSK(char *str)\r
70{\r
71 UsbCommand c;\r
72 c.cmd = CMD_HID_DEMOD_FSK;\r
73 SendCommand(&c, FALSE);\r
74}\r
75\r
76static void CmdTune(char *str)\r
77{\r
78 UsbCommand c;\r
79 c.cmd = CMD_MEASURE_ANTENNA_TUNING;\r
80 SendCommand(&c, FALSE);\r
81}\r
82\r
83static void CmdHi15read(char *str)\r
84{\r
85 UsbCommand c;\r
86 c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693;\r
87 SendCommand(&c, FALSE);\r
88}\r
89\r
90static void CmdHi14read(char *str)\r
91{\r
92 UsbCommand c;\r
93 c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443;\r
94 c.ext1 = atoi(str);\r
95 SendCommand(&c, FALSE);\r
96}\r
97\r
98\r
99/* New command to read the contents of a SRI512 tag\r
100 * SRI512 tags are ISO14443-B modulated memory tags,\r
101 * this command just dumps the contents of the memory/\r
102 */\r
103static void CmdSri512read(char *str)\r
104{\r
105 UsbCommand c;\r
106 c.cmd = CMD_READ_SRI512_TAG;\r
107 c.ext1 = atoi(str);\r
108 SendCommand(&c, FALSE);\r
109}\r
110\r
111// ## New command\r
112static void CmdHi14areader(char *str)\r
113{\r
114 UsbCommand c;\r
115 c.cmd = CMD_READER_ISO_14443a;\r
116 c.ext1 = atoi(str);\r
117 SendCommand(&c, FALSE);\r
118}\r
119\r
120// ## New command\r
121static void CmdHi15reader(char *str)\r
122{\r
123 UsbCommand c;\r
124 c.cmd = CMD_READER_ISO_15693;\r
125 c.ext1 = atoi(str);\r
126 SendCommand(&c, FALSE);\r
127}\r
128\r
129// ## New command\r
130static void CmdHi15tag(char *str)\r
131{\r
132 UsbCommand c;\r
133 c.cmd = CMD_SIMTAG_ISO_15693;\r
134 c.ext1 = atoi(str);\r
135 SendCommand(&c, FALSE);\r
136}\r
137\r
138static void CmdHi14read_sim(char *str)\r
139{\r
140 UsbCommand c;\r
141 c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443_SIM;\r
142 c.ext1 = atoi(str);\r
143 SendCommand(&c, FALSE);\r
144}\r
145\r
146static void CmdHi14readt(char *str)\r
147{\r
148 UsbCommand c;\r
149 c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443;\r
150 c.ext1 = atoi(str);\r
151 SendCommand(&c, FALSE);\r
152\r
153 //CmdHisamplest(str);\r
154 while(CmdHisamplest(str,atoi(str))==0) {\r
155 c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443;\r
156 c.ext1 = atoi(str);\r
157 SendCommand(&c, FALSE);\r
158 }\r
159 RepaintGraphWindow();\r
160}\r
161\r
162static void CmdHisimlisten(char *str)\r
163{\r
164 UsbCommand c;\r
165 c.cmd = CMD_SIMULATE_TAG_HF_LISTEN;\r
166 SendCommand(&c, FALSE);\r
167}\r
168\r
169static void CmdHi14sim(char *str)\r
170{\r
171 UsbCommand c;\r
172 c.cmd = CMD_SIMULATE_TAG_ISO_14443;\r
173 SendCommand(&c, FALSE);\r
174}\r
175\r
176static void CmdHi14asim(char *str) // ## simulate iso14443a tag\r
177{ // ## greg - added ability to specify tag UID\r
178\r
179 unsigned int hi=0, lo=0;\r
180 int n=0, i=0;\r
181 UsbCommand c;\r
182\r
183 while (sscanf(&str[i++], "%1x", &n ) == 1) {\r
184 hi=(hi<<4)|(lo>>28);\r
185 lo=(lo<<4)|(n&0xf);\r
186 }\r
187\r
188 c.cmd = CMD_SIMULATE_TAG_ISO_14443a;\r
189 // c.ext should be set to *str or convert *str to the correct format for a uid\r
190 c.ext1 = hi;\r
191 c.ext2 = lo;\r
192 PrintToScrollback("Emulating 14443A TAG with UID %x%16x", hi, lo);\r
193 SendCommand(&c, FALSE);\r
194}\r
195\r
196static void CmdHi14snoop(char *str)\r
197{\r
198 UsbCommand c;\r
199 c.cmd = CMD_SNOOP_ISO_14443;\r
200 SendCommand(&c, FALSE);\r
201}\r
202\r
203static void CmdHi14asnoop(char *str)\r
204{\r
205 UsbCommand c;\r
206 c.cmd = CMD_SNOOP_ISO_14443a;\r
207 SendCommand(&c, FALSE);\r
208}\r
209\r
210static void CmdFPGAOff(char *str) // ## FPGA Control\r
211{\r
212 UsbCommand c;\r
213 c.cmd = CMD_FPGA_MAJOR_MODE_OFF;\r
214 SendCommand(&c, FALSE);\r
215}\r
216\r
217/* clear out our graph window */\r
218int CmdClearGraph(int redraw)\r
219{\r
220 int gtl = GraphTraceLen;\r
221 GraphTraceLen = 0;\r
222\r
223 if (redraw)\r
224 RepaintGraphWindow();\r
225\r
226 return gtl;\r
227}\r
228\r
229/* write a bit to the graph */\r
230static void CmdAppendGraph(int redraw, int clock, int bit)\r
231{\r
232 int i;\r
233\r
234 for (i = 0; i < (int)(clock/2); i++)\r
235 GraphBuffer[GraphTraceLen++] = bit ^ 1;\r
236\r
237 for (i = (int)(clock/2); i < clock; i++)\r
238 GraphBuffer[GraphTraceLen++] = bit;\r
239\r
240 if (redraw)\r
241 RepaintGraphWindow();\r
242}\r
243\r
244/* Function is equivalent of loread + losamples + em410xread\r
245 * looped until an EM410x tag is detected */\r
246static void CmdEM410xwatch(char *str)\r
247{\r
248 char *zero = "";\r
249 char *twok = "2000";\r
250 go = 1;\r
251\r
252 do\r
253 {\r
254 CmdLoread(zero);\r
255 CmdLosamples(twok);\r
256 CmdEM410xread(zero);\r
257 } while (go);\r
258}\r
259\r
260/* Read the transmitted data of an EM4x50 tag\r
261 * Format:\r
262 *\r
263 * XXXXXXXX [row parity bit (even)] <- 8 bits plus parity\r
264 * XXXXXXXX [row parity bit (even)] <- 8 bits plus parity\r
265 * XXXXXXXX [row parity bit (even)] <- 8 bits plus parity\r
266 * XXXXXXXX [row parity bit (even)] <- 8 bits plus parity\r
267 * CCCCCCCC <- column parity bits\r
268 * 0 <- stop bit\r
269 * LW <- Listen Window\r
270 *\r
271 * This pattern repeats for every block of data being transmitted.\r
272 * Transmission starts with two Listen Windows (LW - a modulated\r
273 * pattern of 320 cycles each (32/32/128/64/64)).\r
274 *\r
275 * Note that this data may or may not be the UID. It is whatever data\r
276 * is stored in the blocks defined in the control word First and Last\r
277 * Word Read values. UID is stored in block 32.\r
278 */\r
279static void CmdEM4x50read(char *str)\r
280{\r
281 int i, j, startblock, clock, skip, block, start, end, low, high;\r
282 BOOL complete= FALSE;\r
283 int tmpbuff[MAX_GRAPH_TRACE_LEN / 64];\r
284 char tmp[6];\r
285\r
286 high= low= 0;\r
287 clock= 64;\r
288\r
289 /* first get high and low values */\r
290 for (i = 0; i < GraphTraceLen; i++)\r
291 {\r
292 if (GraphBuffer[i] > high)\r
293 high = GraphBuffer[i];\r
294 else if (GraphBuffer[i] < low)\r
295 low = GraphBuffer[i];\r
296 }\r
297\r
298 /* populate a buffer with pulse lengths */\r
299 i= 0;\r
300 j= 0;\r
301 while(i < GraphTraceLen)\r
302 {\r
303 // measure from low to low\r
304 while((GraphBuffer[i] > low) && (i<GraphTraceLen))\r
305 ++i;\r
306 start= i;\r
307 while((GraphBuffer[i] < high) && (i<GraphTraceLen))\r
308 ++i;\r
309 while((GraphBuffer[i] > low) && (i<GraphTraceLen))\r
310 ++i;\r
311 if (j>(MAX_GRAPH_TRACE_LEN/64)) {\r
312 break;\r
313 }\r
314 tmpbuff[j++]= i - start;\r
315 }\r
316\r
317 /* look for data start - should be 2 pairs of LW (pulses of 192,128) */\r
318 start= -1;\r
319 skip= 0;\r
320 for (i= 0; i < j - 4 ; ++i)\r
321 {\r
322 skip += tmpbuff[i];\r
323 if (tmpbuff[i] >= 190 && tmpbuff[i] <= 194)\r
324 if (tmpbuff[i+1] >= 126 && tmpbuff[i+1] <= 130)\r
325 if (tmpbuff[i+2] >= 190 && tmpbuff[i+2] <= 194)\r
326 if (tmpbuff[i+3] >= 126 && tmpbuff[i+3] <= 130)\r
327 {\r
328 start= i + 3;\r
329 break;\r
330 }\r
331 }\r
332 startblock= i + 3;\r
333\r
334 /* skip over the remainder of the LW */\r
335 skip += tmpbuff[i+1]+tmpbuff[i+2];\r
336 while(skip < MAX_GRAPH_TRACE_LEN && GraphBuffer[skip] > low)\r
337 ++skip;\r
338 skip += 8;\r
339\r
340 /* now do it again to find the end */\r
341 end= start;\r
342 for (i += 3; i < j - 4 ; ++i)\r
343 {\r
344 end += tmpbuff[i];\r
345 if (tmpbuff[i] >= 190 && tmpbuff[i] <= 194)\r
346 if (tmpbuff[i+1] >= 126 && tmpbuff[i+1] <= 130)\r
347 if (tmpbuff[i+2] >= 190 && tmpbuff[i+2] <= 194)\r
348 if (tmpbuff[i+3] >= 126 && tmpbuff[i+3] <= 130)\r
349 {\r
350 complete= TRUE;\r
351 break;\r
352 }\r
353 }\r
354\r
355 if (start >= 0)\r
356 PrintToScrollback("Found data at sample: %i",skip);\r
357 else\r
358 {\r
359 PrintToScrollback("No data found!");\r
360 PrintToScrollback("Try again with more samples.");\r
361 return;\r
362 }\r
363\r
364 if (!complete)\r
365 {\r
366 PrintToScrollback("*** Warning!");\r
367 PrintToScrollback("Partial data - no end found!");\r
368 PrintToScrollback("Try again with more samples.");\r
369 }\r
370\r
371 /* get rid of leading crap */\r
372 sprintf(tmp,"%i",skip);\r
373 CmdLtrim(tmp);\r
374\r
375 /* now work through remaining buffer printing out data blocks */\r
376 block= 0;\r
377 i= startblock;\r
378 while(block < 6)\r
379 {\r
380 PrintToScrollback("Block %i:", block);\r
381 // mandemod routine needs to be split so we can call it for data\r
382 // just print for now for debugging\r
383 Cmdmanchesterdemod("i 64");\r
384 skip= 0;\r
385 /* look for LW before start of next block */\r
386 for ( ; i < j - 4 ; ++i)\r
387 {\r
388 skip += tmpbuff[i];\r
389 if (tmpbuff[i] >= 190 && tmpbuff[i] <= 194)\r
390 if (tmpbuff[i+1] >= 126 && tmpbuff[i+1] <= 130)\r
391 break;\r
392 }\r
393 while(GraphBuffer[skip] > low)\r
394 ++skip;\r
395 skip += 8;\r
396 sprintf(tmp,"%i",skip);\r
397 CmdLtrim(tmp);\r
398 start += skip;\r
399 block++;\r
400 }\r
401}\r
402\r
403\r
404/* Read the ID of an EM410x tag.\r
405 * Format:\r
406 * 1111 1111 1 <-- standard non-repeatable header\r
407 * XXXX [row parity bit] <-- 10 rows of 5 bits for our 40 bit tag ID\r
408 * ....\r
409 * CCCC <-- each bit here is parity for the 10 bits above in corresponding column\r
410 * 0 <-- stop bit, end of tag\r
411 */\r
412static void CmdEM410xread(char *str)\r
413{\r
414 int i, j, clock, header, rows, bit, hithigh, hitlow, first, bit2idx, high, low;\r
415 int parity[4];\r
416 char id[11];\r
417 int retested = 0;\r
418 int BitStream[MAX_GRAPH_TRACE_LEN];\r
419 high = low = 0;\r
420\r
421 /* Detect high and lows and clock */\r
422 for (i = 0; i < GraphTraceLen; i++)\r
423 {\r
424 if (GraphBuffer[i] > high)\r
425 high = GraphBuffer[i];\r
426 else if (GraphBuffer[i] < low)\r
427 low = GraphBuffer[i];\r
428 }\r
429\r
430 /* get clock */\r
431 clock = GetClock(str, high);\r
432\r
433 /* parity for our 4 columns */\r
434 parity[0] = parity[1] = parity[2] = parity[3] = 0;\r
435 header = rows = 0;\r
436\r
437 /* manchester demodulate */\r
438 bit = bit2idx = 0;\r
439 for (i = 0; i < (int)(GraphTraceLen / clock); i++)\r
440 {\r
441 hithigh = 0;\r
442 hitlow = 0;\r
443 first = 1;\r
444\r
445 /* Find out if we hit both high and low peaks */\r
446 for (j = 0; j < clock; j++)\r
447 {\r
448 if (GraphBuffer[(i * clock) + j] == high)\r
449 hithigh = 1;\r
450 else if (GraphBuffer[(i * clock) + j] == low)\r
451 hitlow = 1;\r
452\r
453 /* it doesn't count if it's the first part of our read\r
454 because it's really just trailing from the last sequence */\r
455 if (first && (hithigh || hitlow))\r
456 hithigh = hitlow = 0;\r
457 else\r
458 first = 0;\r
459\r
460 if (hithigh && hitlow)\r
461 break;\r
462 }\r
463\r
464 /* If we didn't hit both high and low peaks, we had a bit transition */\r
465 if (!hithigh || !hitlow)\r
466 bit ^= 1;\r
467\r
468 BitStream[bit2idx++] = bit;\r
469 }\r
470\r
471retest:\r
472 /* We go till 5 before the graph ends because we'll get that far below */\r
473 for (i = 1; i < bit2idx - 5; i++)\r
474 {\r
475 /* Step 2: We have our header but need our tag ID */\r
476 if (header == 9 && rows < 10)\r
477 {\r
478 /* Confirm parity is correct */\r
479 if ((BitStream[i] ^ BitStream[i+1] ^ BitStream[i+2] ^ BitStream[i+3]) == BitStream[i+4])\r
480 {\r
481 /* Read another byte! */\r
482 sprintf(id+rows, "%x", (8 * BitStream[i]) + (4 * BitStream[i+1]) + (2 * BitStream[i+2]) + (1 * BitStream[i+3]));\r
483 rows++;\r
484\r
485 /* Keep parity info */\r
486 parity[0] ^= BitStream[i];\r
487 parity[1] ^= BitStream[i+1];\r
488 parity[2] ^= BitStream[i+2];\r
489 parity[3] ^= BitStream[i+3];\r
490\r
491 /* Move 4 bits ahead */\r
492 i += 4;\r
493 }\r
494\r
495 /* Damn, something wrong! reset */\r
496 else\r
497 {\r
498 PrintToScrollback("Thought we had a valid tag but failed at word %d (i=%d)", rows + 1, i);\r
499\r
500 /* Start back rows * 5 + 9 header bits, -1 to not start at same place */\r
501 i -= 9 + (5 * rows) - 5;\r
502\r
503 rows = header = 0;\r
504 }\r
505 }\r
506\r
507 /* Step 3: Got our 40 bits! confirm column parity */\r
508 else if (rows == 10)\r
509 {\r
510 /* We need to make sure our 4 bits of parity are correct and we have a stop bit */\r
511 if (BitStream[i] == parity[0] && BitStream[i+1] == parity[1] &&\r
512 BitStream[i+2] == parity[2] && BitStream[i+3] == parity[3] &&\r
513 BitStream[i+4] == 0)\r
514 {\r
515 /* Sweet! */\r
516 PrintToScrollback("EM410x Tag ID: %s", id);\r
517\r
518 /* Stop any loops */\r
519 go = 0;\r
520 return;\r
521 }\r
522\r
523 /* Crap! Incorrect parity or no stop bit, start all over */\r
524 else\r
525 {\r
526 rows = header = 0;\r
527\r
528 /* Go back 59 bits (9 header bits + 10 rows at 4+1 parity) */\r
529 i -= 59;\r
530 }\r
531 }\r
532\r
533 /* Step 1: get our header */\r
534 else if (header < 9)\r
535 {\r
536 /* Need 9 consecutive 1's */\r
537 if (BitStream[i] == 1)\r
538 header++;\r
539\r
540 /* We don't have a header, not enough consecutive 1 bits */\r
541 else\r
542 header = 0;\r
543 }\r
544 }\r
545\r
546 /* if we've already retested after flipping bits, return */\r
547 if (retested++)\r
548 return;\r
549\r
550 /* if this didn't work, try flipping bits */\r
551 for (i = 0; i < bit2idx; i++)\r
552 BitStream[i] ^= 1;\r
553\r
554 goto retest;\r
555}\r
556\r
557/* emulate an EM410X tag\r
558 * Format:\r
559 * 1111 1111 1 <-- standard non-repeatable header\r
560 * XXXX [row parity bit] <-- 10 rows of 5 bits for our 40 bit tag ID\r
561 * ....\r
562 * CCCC <-- each bit here is parity for the 10 bits above in corresponding column\r
563 * 0 <-- stop bit, end of tag\r
564 */\r
565static void CmdEM410xsim(char *str)\r
566{\r
567 int i, n, j, h, binary[4], parity[4];\r
568 char *s = "0";\r
569\r
570 /* clock is 64 in EM410x tags */\r
571 int clock = 64;\r
572\r
573 /* clear our graph */\r
574 CmdClearGraph(0);\r
575\r
576 /* write it out a few times */\r
577 for (h = 0; h < 4; h++)\r
578 {\r
579 /* write 9 start bits */\r
580 for (i = 0; i < 9; i++)\r
581 CmdAppendGraph(0, clock, 1);\r
582\r
583 /* for each hex char */\r
584 parity[0] = parity[1] = parity[2] = parity[3] = 0;\r
585 for (i = 0; i < 10; i++)\r
586 {\r
587 /* read each hex char */\r
588 sscanf(&str[i], "%1x", &n);\r
589 for (j = 3; j >= 0; j--, n/= 2)\r
590 binary[j] = n % 2;\r
591\r
592 /* append each bit */\r
593 CmdAppendGraph(0, clock, binary[0]);\r
594 CmdAppendGraph(0, clock, binary[1]);\r
595 CmdAppendGraph(0, clock, binary[2]);\r
596 CmdAppendGraph(0, clock, binary[3]);\r
597\r
598 /* append parity bit */\r
599 CmdAppendGraph(0, clock, binary[0] ^ binary[1] ^ binary[2] ^ binary[3]);\r
600\r
601 /* keep track of column parity */\r
602 parity[0] ^= binary[0];\r
603 parity[1] ^= binary[1];\r
604 parity[2] ^= binary[2];\r
605 parity[3] ^= binary[3];\r
606 }\r
607\r
608 /* parity columns */\r
609 CmdAppendGraph(0, clock, parity[0]);\r
610 CmdAppendGraph(0, clock, parity[1]);\r
611 CmdAppendGraph(0, clock, parity[2]);\r
612 CmdAppendGraph(0, clock, parity[3]);\r
613\r
614 /* stop bit */\r
615 CmdAppendGraph(0, clock, 0);\r
616 }\r
617\r
618 /* modulate that biatch */\r
619 Cmdmanchestermod(s);\r
620\r
621 /* booyah! */\r
622 RepaintGraphWindow();\r
623\r
624 CmdLosim(s);\r
625}\r
626\r
627static void ChkBitstream(char *str)\r
628{\r
629 int i;\r
630\r
631 /* convert to bitstream if necessary */\r
632 for (i = 0; i < (int)(GraphTraceLen / 2); i++)\r
633 {\r
634 if (GraphBuffer[i] > 1 || GraphBuffer[i] < 0)\r
635 {\r
636 Cmdbitstream(str);\r
637 break;\r
638 }\r
639 }\r
640}\r
641\r
642static void CmdLosim(char *str)\r
643{\r
644 int i;\r
645\r
646 /* convert to bitstream if necessary */\r
647 ChkBitstream(str);\r
648\r
649 for (i = 0; i < GraphTraceLen; i += 48) {\r
650 UsbCommand c;\r
651 int j;\r
652 for(j = 0; j < 48; j++) {\r
653 c.d.asBytes[j] = GraphBuffer[i+j];\r
654 }\r
655 c.cmd = CMD_DOWNLOADED_SIM_SAMPLES_125K;\r
656 c.ext1 = i;\r
657 SendCommand(&c, FALSE);\r
658 }\r
659\r
660 UsbCommand c;\r
661 c.cmd = CMD_SIMULATE_TAG_125K;\r
662 c.ext1 = GraphTraceLen;\r
663 SendCommand(&c, FALSE);\r
664}\r
665\r
666static void CmdLosimBidir(char *str)\r
667{\r
668 UsbCommand c;\r
669 c.cmd = CMD_LF_SIMULATE_BIDIR;\r
670 c.ext1 = 47; /* Set ADC to twice the carrier for a slight supersampling */\r
671 c.ext2 = 384;\r
672 SendCommand(&c, FALSE);\r
673}\r
674\r
675static void CmdLoread(char *str)\r
676{\r
677 UsbCommand c;\r
678 // 'h' means higher-low-frequency, 134 kHz\r
679 if(*str == 'h') {\r
680 c.ext1 = 1;\r
681 } else if (*str == '\0') {\r
682 c.ext1 = 0;\r
683 } else {\r
684 PrintToScrollback("use 'loread' or 'loread h'");\r
685 return;\r
686 }\r
687 c.cmd = CMD_ACQUIRE_RAW_ADC_SAMPLES_125K;\r
688 SendCommand(&c, FALSE);\r
689}\r
690\r
691static void CmdDetectReader(char *str)\r
692{\r
693 UsbCommand c;\r
694 // 'l' means LF - 125/134 kHz\r
695 if(*str == 'l') {\r
696 c.ext1 = 1;\r
697 } else if (*str == 'h') {\r
698 c.ext1 = 2;\r
699 } else if (*str != '\0') {\r
700 PrintToScrollback("use 'detectreader' or 'detectreader l' or 'detectreader h'");\r
701 return;\r
702 }\r
703 c.cmd = CMD_LISTEN_READER_FIELD;\r
704 SendCommand(&c, FALSE);\r
705}\r
706\r
707/* send a command before reading */\r
708static void CmdLoCommandRead(char *str)\r
709{\r
710 static char dummy[3];\r
711\r
712 dummy[0]= ' ';\r
713\r
714 UsbCommand c;\r
715 c.cmd = CMD_MOD_THEN_ACQUIRE_RAW_ADC_SAMPLES_125K;\r
716 sscanf(str, "%i %i %i %s %s", &c.ext1, &c.ext2, &c.ext3, (char *) &c.d.asBytes,(char *) &dummy+1);\r
717 // in case they specified 'h'\r
718 strcpy((char *)&c.d.asBytes + strlen((char *)c.d.asBytes), dummy);\r
719 SendCommand(&c, FALSE);\r
720}\r
721\r
722static void CmdLosamples(char *str)\r
723{\r
724 int cnt = 0;\r
725 int i;\r
726 int n;\r
727\r
728 n=atoi(str);\r
729 if (n==0) n=128;\r
730 if (n>16000) n=16000;\r
731\r
732 for(i = 0; i < n; i += 12) {\r
733 UsbCommand c;\r
734 c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K;\r
735 c.ext1 = i;\r
736 SendCommand(&c, FALSE);\r
737 ReceiveCommand(&c);\r
738 if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) {\r
739 if (!go)\r
740 PrintToScrollback("bad resp");\r
741 return;\r
742 }\r
743 int j;\r
744 for(j = 0; j < 48; j++) {\r
745 GraphBuffer[cnt++] = ((int)c.d.asBytes[j]) - 128;\r
746 }\r
747 }\r
748 GraphTraceLen = n*4;\r
749 RepaintGraphWindow();\r
750}\r
751\r
752static void CmdBitsamples(char *str)\r
753{\r
754 int cnt = 0;\r
755 int i;\r
756 int n;\r
757\r
758 n = 3072;\r
759 for(i = 0; i < n; i += 12) {\r
760 UsbCommand c;\r
761 c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K;\r
762 c.ext1 = i;\r
763 SendCommand(&c, FALSE);\r
764 ReceiveCommand(&c);\r
765 if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) {\r
766 PrintToScrollback("bad resp");\r
767 return;\r
768 }\r
769 int j, k;\r
770 for(j = 0; j < 48; j++) {\r
771 for(k = 0; k < 8; k++) {\r
772 if(c.d.asBytes[j] & (1 << (7 - k))) {\r
773 GraphBuffer[cnt++] = 1;\r
774 } else {\r
775 GraphBuffer[cnt++] = 0;\r
776 }\r
777 }\r
778 }\r
779 }\r
780 GraphTraceLen = cnt;\r
781 RepaintGraphWindow();\r
782}\r
783\r
784static void CmdHisamples(char *str)\r
785{\r
786 int cnt = 0;\r
787 int i;\r
788 int n;\r
789 n = 1000;\r
790 for(i = 0; i < n; i += 12) {\r
791 UsbCommand c;\r
792 c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K;\r
793 c.ext1 = i;\r
794 SendCommand(&c, FALSE);\r
795 ReceiveCommand(&c);\r
796 if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) {\r
797 PrintToScrollback("bad resp");\r
798 return;\r
799 }\r
800 int j;\r
801 for(j = 0; j < 48; j++) {\r
802 GraphBuffer[cnt++] = (int)((BYTE)c.d.asBytes[j]);\r
803 }\r
804 }\r
805 GraphTraceLen = n*4;\r
806\r
807 RepaintGraphWindow();\r
808}\r
809\r
810static int CmdHisamplest(char *str, int nrlow)\r
811{\r
812 int cnt = 0;\r
813 int t1, t2;\r
814 int i;\r
815 int n;\r
816 int hasbeennull;\r
817 int show;\r
818\r
819\r
820 n = 1000;\r
821 hasbeennull = 0;\r
822 for(i = 0; i < n; i += 12) {\r
823 UsbCommand c;\r
824 c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K;\r
825 c.ext1 = i;\r
826 SendCommand(&c, FALSE);\r
827 ReceiveCommand(&c);\r
828 if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) {\r
829 PrintToScrollback("bad resp");\r
830 return 0;\r
831 }\r
832 int j;\r
833 for(j = 0; j < 48; j++) {\r
834 t2 = (int)((BYTE)c.d.asBytes[j]);\r
835 if((t2 ^ 0xC0) & 0xC0) { hasbeennull++; }\r
836\r
837 show = 0;\r
838 switch(show) {\r
839 case 0:\r
840 // combined\r
841 t1 = (t2 & 0x80) ^ (t2 & 0x20);\r
842 t2 = ((t2 << 1) & 0x80) ^ ((t2 << 1) & 0x20);\r
843 break;\r
844\r
845 case 1:\r
846 // only reader\r
847 t1 = (t2 & 0x80);\r
848 t2 = ((t2 << 1) & 0x80);\r
849 break;\r
850\r
851 case 2:\r
852 // only tag\r
853 t1 = (t2 & 0x20);\r
854 t2 = ((t2 << 1) & 0x20);\r
855 break;\r
856\r
857 case 3:\r
858 // both, but tag with other algorithm\r
859 t1 = (t2 & 0x80) ^ (t2 & 0x08);\r
860 t2 = ((t2 << 1) & 0x80) ^ ((t2 << 1) & 0x08);\r
861 break;\r
862 }\r
863\r
864 GraphBuffer[cnt++] = t1;\r
865 GraphBuffer[cnt++] = t2;\r
866 }\r
867 }\r
868 GraphTraceLen = n*4;\r
869// 1130\r
870 if(hasbeennull>nrlow || nrlow==0) {\r
871 PrintToScrollback("hasbeennull=%d", hasbeennull);\r
872 return 1;\r
873 }\r
874 else {\r
875 return 0;\r
876 }\r
877}\r
878\r
879\r
880static void CmdHexsamples(char *str)\r
881{\r
882 int i;\r
883 int n;\r
884\r
885 if(atoi(str) == 0) {\r
886 n = 12;\r
887 } else {\r
888 n = atoi(str)/4;\r
889 }\r
890\r
891 for(i = 0; i < n; i += 12) {\r
892 UsbCommand c;\r
893 c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K;\r
894 c.ext1 = i;\r
895 SendCommand(&c, FALSE);\r
896 ReceiveCommand(&c);\r
897 if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) {\r
898 PrintToScrollback("bad resp");\r
899 return;\r
900 }\r
901 int j;\r
902 for(j = 0; j < 48; j += 8) {\r
903 PrintToScrollback("%02x %02x %02x %02x %02x %02x %02x %02x",\r
904 c.d.asBytes[j+0],\r
905 c.d.asBytes[j+1],\r
906 c.d.asBytes[j+2],\r
907 c.d.asBytes[j+3],\r
908 c.d.asBytes[j+4],\r
909 c.d.asBytes[j+5],\r
910 c.d.asBytes[j+6],\r
911 c.d.asBytes[j+7],\r
912 c.d.asBytes[j+8]\r
913 );\r
914 }\r
915 }\r
916}\r
917\r
918static void CmdHisampless(char *str)\r
919{\r
920 int cnt = 0;\r
921 int i;\r
922 int n;\r
923\r
924 if(atoi(str) == 0) {\r
925 n = 1000;\r
926 } else {\r
927 n = atoi(str)/4;\r
928 }\r
929\r
930 for(i = 0; i < n; i += 12) {\r
931 UsbCommand c;\r
932 c.cmd = CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K;\r
933 c.ext1 = i;\r
934 SendCommand(&c, FALSE);\r
935 ReceiveCommand(&c);\r
936 if(c.cmd != CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) {\r
937 PrintToScrollback("bad resp");\r
938 return;\r
939 }\r
940 int j;\r
941 for(j = 0; j < 48; j++) {\r
942 GraphBuffer[cnt++] = (int)((signed char)c.d.asBytes[j]);\r
943 }\r
944 }\r
945 GraphTraceLen = cnt;\r
946\r
947 RepaintGraphWindow();\r
948}\r
949\r
950static WORD Iso15693Crc(BYTE *v, int n)\r
951{\r
952 DWORD reg;\r
953 int i, j;\r
954\r
955 reg = 0xffff;\r
956 for(i = 0; i < n; i++) {\r
957 reg = reg ^ ((DWORD)v[i]);\r
958 for (j = 0; j < 8; j++) {\r
959 if (reg & 0x0001) {\r
960 reg = (reg >> 1) ^ 0x8408;\r
961 } else {\r
962 reg = (reg >> 1);\r
963 }\r
964 }\r
965 }\r
966\r
967 return (WORD)~reg;\r
968}\r
969\r
970static void CmdHi14bdemod(char *str)\r
971{\r
972 int i, j, iold;\r
973 int isum, qsum;\r
974 int outOfWeakAt;\r
975 BOOL negateI, negateQ;\r
976\r
977 BYTE data[256];\r
978 int dataLen=0;\r
979\r
980 // As received, the samples are pairs, correlations against I and Q\r
981 // square waves. So estimate angle of initial carrier (or just\r
982 // quadrant, actually), and then do the demod.\r
983\r
984 // First, estimate where the tag starts modulating.\r
985 for(i = 0; i < GraphTraceLen; i += 2) {\r
986 if(abs(GraphBuffer[i]) + abs(GraphBuffer[i+1]) > 40) {\r
987 break;\r
988 }\r
989 }\r
990 if(i >= GraphTraceLen) {\r
991 PrintToScrollback("too weak to sync");\r
992 return;\r
993 }\r
994 PrintToScrollback("out of weak at %d", i);\r
995 outOfWeakAt = i;\r
996\r
997 // Now, estimate the phase in the initial modulation of the tag\r
998 isum = 0;\r
999 qsum = 0;\r
1000 for(; i < (outOfWeakAt + 16); i += 2) {\r
1001 isum += GraphBuffer[i+0];\r
1002 qsum += GraphBuffer[i+1];\r
1003 }\r
1004 negateI = (isum < 0);\r
1005 negateQ = (qsum < 0);\r
1006\r
1007 // Turn the correlation pairs into soft decisions on the bit.\r
1008 j = 0;\r
1009 for(i = 0; i < GraphTraceLen/2; i++) {\r
1010 int si = GraphBuffer[j];\r
1011 int sq = GraphBuffer[j+1];\r
1012 if(negateI) si = -si;\r
1013 if(negateQ) sq = -sq;\r
1014 GraphBuffer[i] = si + sq;\r
1015 j += 2;\r
1016 }\r
1017 GraphTraceLen = i;\r
1018\r
1019 i = outOfWeakAt/2;\r
1020 while(GraphBuffer[i] > 0 && i < GraphTraceLen)\r
1021 i++;\r
1022 if(i >= GraphTraceLen) goto demodError;\r
1023\r
1024 iold = i;\r
1025 while(GraphBuffer[i] < 0 && i < GraphTraceLen)\r
1026 i++;\r
1027 if(i >= GraphTraceLen) goto demodError;\r
1028 if((i - iold) > 23) goto demodError;\r
1029\r
1030 PrintToScrollback("make it to demod loop");\r
1031\r
1032 for(;;) {\r
1033 iold = i;\r
1034 while(GraphBuffer[i] >= 0 && i < GraphTraceLen)\r
1035 i++;\r
1036 if(i >= GraphTraceLen) goto demodError;\r
1037 if((i - iold) > 6) goto demodError;\r
1038\r
1039 WORD shiftReg = 0;\r
1040 if(i + 20 >= GraphTraceLen) goto demodError;\r
1041\r
1042 for(j = 0; j < 10; j++) {\r
1043 int soft = GraphBuffer[i] + GraphBuffer[i+1];\r
1044\r
1045 if(abs(soft) < ((abs(isum) + abs(qsum))/20)) {\r
1046 PrintToScrollback("weak bit");\r
1047 }\r
1048\r
1049 shiftReg >>= 1;\r
1050 if(GraphBuffer[i] + GraphBuffer[i+1] >= 0) {\r
1051 shiftReg |= 0x200;\r
1052 }\r
1053\r
1054 i+= 2;\r
1055 }\r
1056\r
1057 if( (shiftReg & 0x200) &&\r
1058 !(shiftReg & 0x001))\r
1059 {\r
1060 // valid data byte, start and stop bits okay\r
1061 PrintToScrollback(" %02x", (shiftReg >> 1) & 0xff);\r
1062 data[dataLen++] = (shiftReg >> 1) & 0xff;\r
1063 if(dataLen >= sizeof(data)) {\r
1064 return;\r
1065 }\r
1066 } else if(shiftReg == 0x000) {\r
1067 // this is EOF\r
1068 break;\r
1069 } else {\r
1070 goto demodError;\r
1071 }\r
1072 }\r
1073\r
1074 BYTE first, second;\r
1075 ComputeCrc14443(CRC_14443_B, data, dataLen-2, &first, &second);\r
1076 PrintToScrollback("CRC: %02x %02x (%s)\n", first, second,\r
1077 (first == data[dataLen-2] && second == data[dataLen-1]) ?\r
1078 "ok" : "****FAIL****");\r
1079\r
1080 RepaintGraphWindow();\r
1081 return;\r
1082\r
1083demodError:\r
1084 PrintToScrollback("demod error");\r
1085 RepaintGraphWindow();\r
1086}\r
1087\r
1088static void CmdHi14list(char *str)\r
1089{\r
1090 BYTE got[960];\r
1091 GetFromBigBuf(got, sizeof(got));\r
1092\r
1093 PrintToScrollback("recorded activity:");\r
1094 PrintToScrollback(" time :rssi: who bytes");\r
1095 PrintToScrollback("---------+----+----+-----------");\r
1096\r
1097 int i = 0;\r
1098 int prev = -1;\r
1099\r
1100 for(;;) {\r
1101 if(i >= 900) {\r
1102 break;\r
1103 }\r
1104\r
1105 BOOL isResponse;\r
1106 int timestamp = *((DWORD *)(got+i));\r
1107 if(timestamp & 0x80000000) {\r
1108 timestamp &= 0x7fffffff;\r
1109 isResponse = 1;\r
1110 } else {\r
1111 isResponse = 0;\r
1112 }\r
1113 int metric = *((DWORD *)(got+i+4));\r
1114\r
1115 int len = got[i+8];\r
1116\r
1117 if(len > 100) {\r
1118 break;\r
1119 }\r
1120 if(i + len >= 900) {\r
1121 break;\r
1122 }\r
1123\r
1124 BYTE *frame = (got+i+9);\r
1125\r
1126 char line[1000] = "";\r
1127 int j;\r
1128 for(j = 0; j < len; j++) {\r
1129 sprintf(line+(j*3), "%02x ", frame[j]);\r
1130 }\r
1131\r
1132 char *crc;\r
1133 if(len > 2) {\r
1134 BYTE b1, b2;\r
1135 ComputeCrc14443(CRC_14443_B, frame, len-2, &b1, &b2);\r
1136 if(b1 != frame[len-2] || b2 != frame[len-1]) {\r
1137 crc = "**FAIL CRC**";\r
1138 } else {\r
1139 crc = "";\r
1140 }\r
1141 } else {\r
1142 crc = "(SHORT)";\r
1143 }\r
1144\r
1145 char metricString[100];\r
1146 if(isResponse) {\r
1147 sprintf(metricString, "%3d", metric);\r
1148 } else {\r
1149 strcpy(metricString, " ");\r
1150 }\r
1151\r
1152 PrintToScrollback(" +%7d: %s: %s %s %s",\r
1153 (prev < 0 ? 0 : timestamp - prev),\r
1154 metricString,\r
1155 (isResponse ? "TAG" : " "), line, crc);\r
1156\r
1157 prev = timestamp;\r
1158 i += (len + 9);\r
1159 }\r
1160}\r
1161\r
1162static void CmdHi14alist(char *str)\r
1163{\r
1164 BYTE got[1920];\r
1165 GetFromBigBuf(got, sizeof(got));\r
1166\r
1167 PrintToScrollback("recorded activity:");\r
1168 PrintToScrollback(" ETU :rssi: who bytes");\r
1169 PrintToScrollback("---------+----+----+-----------");\r
1170\r
1171 int i = 0;\r
1172 int prev = -1;\r
1173\r
1174 for(;;) {\r
1175 if(i >= 1900) {\r
1176 break;\r
1177 }\r
1178\r
1179 BOOL isResponse;\r
1180 int timestamp = *((DWORD *)(got+i));\r
1181 if(timestamp & 0x80000000) {\r
1182 timestamp &= 0x7fffffff;\r
1183 isResponse = 1;\r
1184 } else {\r
1185 isResponse = 0;\r
1186 }\r
1187\r
1188 int metric = 0;\r
1189 int parityBits = *((DWORD *)(got+i+4));\r
1190 // 4 bytes of additional information...\r
1191 // maximum of 32 additional parity bit information\r
1192 //\r
1193 // TODO:\r
1194 // at each quarter bit period we can send power level (16 levels)\r
1195 // or each half bit period in 256 levels.\r
1196\r
1197\r
1198 int len = got[i+8];\r
1199\r
1200 if(len > 100) {\r
1201 break;\r
1202 }\r
1203 if(i + len >= 1900) {\r
1204 break;\r
1205 }\r
1206\r
1207 BYTE *frame = (got+i+9);\r
1208\r
1209 // Break and stick with current result if buffer was not completely full\r
1210 if(frame[0] == 0x44 && frame[1] == 0x44 && frame[3] == 0x44) { break; }\r
1211\r
1212 char line[1000] = "";\r
1213 int j;\r
1214 for(j = 0; j < len; j++) {\r
1215 int oddparity = 0x01;\r
1216 int k;\r
1217\r
1218 for(k=0;k<8;k++) {\r
1219 oddparity ^= (((frame[j] & 0xFF) >> k) & 0x01);\r
1220 }\r
1221\r
1222 //if((parityBits >> (len - j - 1)) & 0x01) {\r
1223 if(isResponse && (oddparity != ((parityBits >> (len - j - 1)) & 0x01))) {\r
1224 sprintf(line+(j*4), "%02x! ", frame[j]);\r
1225 }\r
1226 else {\r
1227 sprintf(line+(j*4), "%02x ", frame[j]);\r
1228 }\r
1229 }\r
1230\r
1231 char *crc;\r
1232 crc = "";\r
1233 if(len > 2) {\r
1234 BYTE b1, b2;\r
1235 for(j = 0; j < (len - 1); j++) {\r
1236 // gives problems... search for the reason..\r
1237 /*if(frame[j] == 0xAA) {\r
1238 switch(frame[j+1]) {\r
1239 case 0x01:\r
1240 crc = "[1] Two drops close after each other";\r
1241 break;\r
1242 case 0x02:\r
1243 crc = "[2] Potential SOC with a drop in second half of bitperiod";\r
1244 break;\r
1245 case 0x03:\r
1246 crc = "[3] Segment Z after segment X is not possible";\r
1247 break;\r
1248 case 0x04:\r
1249 crc = "[4] Parity bit of a fully received byte was wrong";\r
1250 break;\r
1251 default:\r
1252 crc = "[?] Unknown error";\r
1253 break;\r
1254 }\r
1255 break;\r
1256 }*/\r
1257 }\r
1258\r
1259 if(strlen(crc)==0) {\r
1260 ComputeCrc14443(CRC_14443_A, frame, len-2, &b1, &b2);\r
1261 if(b1 != frame[len-2] || b2 != frame[len-1]) {\r
1262 crc = (isResponse & (len < 6)) ? "" : " !crc";\r
1263 } else {\r
1264 crc = "";\r
1265 }\r
1266 }\r
1267 } else {\r
1268 crc = ""; // SHORT\r
1269 }\r
1270\r
1271 char metricString[100];\r
1272 if(isResponse) {\r
1273 sprintf(metricString, "%3d", metric);\r
1274 } else {\r
1275 strcpy(metricString, " ");\r
1276 }\r
1277\r
1278 PrintToScrollback(" +%7d: %s: %s %s %s",\r
1279 (prev < 0 ? 0 : (timestamp - prev)),\r
1280 metricString,\r
1281 (isResponse ? "TAG" : " "), line, crc);\r
1282\r
1283 prev = timestamp;\r
1284 i += (len + 9);\r
1285 }\r
1286 CommandFinished = 1;\r
1287}\r
1288\r
1289static void CmdHi15demod(char *str)\r
1290{\r
1291 // The sampling rate is 106.353 ksps/s, for T = 18.8 us\r
1292\r
1293 // SOF defined as\r
1294 // 1) Unmodulated time of 56.64us\r
1295 // 2) 24 pulses of 423.75khz\r
1296 // 3) logic '1' (unmodulated for 18.88us followed by 8 pulses of 423.75khz)\r
1297\r
1298 static const int FrameSOF[] = {\r
1299 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1300 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1301 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
1302 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
1303 -1, -1, -1, -1,\r
1304 -1, -1, -1, -1,\r
1305 1, 1, 1, 1,\r
1306 1, 1, 1, 1\r
1307 };\r
1308 static const int Logic0[] = {\r
1309 1, 1, 1, 1,\r
1310 1, 1, 1, 1,\r
1311 -1, -1, -1, -1,\r
1312 -1, -1, -1, -1\r
1313 };\r
1314 static const int Logic1[] = {\r
1315 -1, -1, -1, -1,\r
1316 -1, -1, -1, -1,\r
1317 1, 1, 1, 1,\r
1318 1, 1, 1, 1\r
1319 };\r
1320\r
1321 // EOF defined as\r
1322 // 1) logic '0' (8 pulses of 423.75khz followed by unmodulated for 18.88us)\r
1323 // 2) 24 pulses of 423.75khz\r
1324 // 3) Unmodulated time of 56.64us\r
1325\r
1326 static const int FrameEOF[] = {\r
1327 1, 1, 1, 1,\r
1328 1, 1, 1, 1,\r
1329 -1, -1, -1, -1,\r
1330 -1, -1, -1, -1,\r
1331 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
1332 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
1333 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1334 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1\r
1335 };\r
1336\r
1337 int i, j;\r
1338 int max = 0, maxPos;\r
1339\r
1340 int skip = 4;\r
1341\r
1342 if(GraphTraceLen < 1000) return;\r
1343\r
1344 // First, correlate for SOF\r
1345 for(i = 0; i < 100; i++) {\r
1346 int corr = 0;\r
1347 for(j = 0; j < arraylen(FrameSOF); j += skip) {\r
1348 corr += FrameSOF[j]*GraphBuffer[i+(j/skip)];\r
1349 }\r
1350 if(corr > max) {\r
1351 max = corr;\r
1352 maxPos = i;\r
1353 }\r
1354 }\r
1355 PrintToScrollback("SOF at %d, correlation %d", maxPos,\r
1356 max/(arraylen(FrameSOF)/skip));\r
1357\r
1358 i = maxPos + arraylen(FrameSOF)/skip;\r
1359 int k = 0;\r
1360 BYTE outBuf[20];\r
1361 memset(outBuf, 0, sizeof(outBuf));\r
1362 BYTE mask = 0x01;\r
1363 for(;;) {\r
1364 int corr0 = 0, corr1 = 0, corrEOF = 0;\r
1365 for(j = 0; j < arraylen(Logic0); j += skip) {\r
1366 corr0 += Logic0[j]*GraphBuffer[i+(j/skip)];\r
1367 }\r
1368 for(j = 0; j < arraylen(Logic1); j += skip) {\r
1369 corr1 += Logic1[j]*GraphBuffer[i+(j/skip)];\r
1370 }\r
1371 for(j = 0; j < arraylen(FrameEOF); j += skip) {\r
1372 corrEOF += FrameEOF[j]*GraphBuffer[i+(j/skip)];\r
1373 }\r
1374 // Even things out by the length of the target waveform.\r
1375 corr0 *= 4;\r
1376 corr1 *= 4;\r
1377\r
1378 if(corrEOF > corr1 && corrEOF > corr0) {\r
1379 PrintToScrollback("EOF at %d", i);\r
1380 break;\r
1381 } else if(corr1 > corr0) {\r
1382 i += arraylen(Logic1)/skip;\r
1383 outBuf[k] |= mask;\r
1384 } else {\r
1385 i += arraylen(Logic0)/skip;\r
1386 }\r
1387 mask <<= 1;\r
1388 if(mask == 0) {\r
1389 k++;\r
1390 mask = 0x01;\r
1391 }\r
1392 if((i+(int)arraylen(FrameEOF)) >= GraphTraceLen) {\r
1393 PrintToScrollback("ran off end!");\r
1394 break;\r
1395 }\r
1396 }\r
1397 if(mask != 0x01) {\r
1398 PrintToScrollback("error, uneven octet! (discard extra bits!)");\r
1399 PrintToScrollback(" mask=%02x", mask);\r
1400 }\r
1401 PrintToScrollback("%d octets", k);\r
1402\r
1403 for(i = 0; i < k; i++) {\r
1404 PrintToScrollback("# %2d: %02x ", i, outBuf[i]);\r
1405 }\r
1406 PrintToScrollback("CRC=%04x", Iso15693Crc(outBuf, k-2));\r
1407}\r
1408\r
1409static void CmdFSKdemod(char *cmdline)\r
1410{\r
1411 static const int LowTone[] = {\r
1412 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,\r
1413 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,\r
1414 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,\r
1415 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,\r
1416 1, 1, 1, 1, 1, -1, -1, -1, -1, -1\r
1417 };\r
1418 static const int HighTone[] = {\r
1419 1, 1, 1, 1, 1, -1, -1, -1, -1,\r
1420 1, 1, 1, 1, -1, -1, -1, -1,\r
1421 1, 1, 1, 1, -1, -1, -1, -1,\r
1422 1, 1, 1, 1, -1, -1, -1, -1,\r
1423 1, 1, 1, 1, -1, -1, -1, -1,\r
1424 1, 1, 1, 1, -1, -1, -1, -1, -1,\r
1425 };\r
1426\r
1427 int lowLen = sizeof(LowTone)/sizeof(int);\r
1428 int highLen = sizeof(HighTone)/sizeof(int);\r
1429 int convLen = (highLen>lowLen)?highLen:lowLen;\r
1430 DWORD hi = 0, lo = 0;\r
1431\r
1432 int i, j;\r
1433 int minMark=0, maxMark=0;\r
1434\r
1435 for(i = 0; i < GraphTraceLen - convLen; i++) {\r
1436 int lowSum = 0, highSum = 0;\r
1437\r
1438 for(j = 0; j < lowLen; j++) {\r
1439 lowSum += LowTone[j]*GraphBuffer[i+j];\r
1440 }\r
1441 for(j = 0; j < highLen; j++) {\r
1442 highSum += HighTone[j]*GraphBuffer[i+j];\r
1443 }\r
1444 lowSum = abs((100*lowSum) / lowLen);\r
1445 highSum = abs((100*highSum) / highLen);\r
1446 GraphBuffer[i] = (highSum << 16) | lowSum;\r
1447 }\r
1448\r
1449 for(i = 0; i < GraphTraceLen - convLen - 16; i++) {\r
1450 int j;\r
1451 int lowTot = 0, highTot = 0;\r
1452 // 10 and 8 are f_s divided by f_l and f_h, rounded\r
1453 for(j = 0; j < 10; j++) {\r
1454 lowTot += (GraphBuffer[i+j] & 0xffff);\r
1455 }\r
1456 for(j = 0; j < 8; j++) {\r
1457 highTot += (GraphBuffer[i+j] >> 16);\r
1458 }\r
1459 GraphBuffer[i] = lowTot - highTot;\r
1460 if (GraphBuffer[i]>maxMark) maxMark=GraphBuffer[i];\r
1461 if (GraphBuffer[i]<minMark) minMark=GraphBuffer[i];\r
1462 }\r
1463\r
1464 GraphTraceLen -= (convLen + 16);\r
1465\r
1466 RepaintGraphWindow();\r
1467\r
1468 // Find bit-sync (3 lo followed by 3 high)\r
1469 int max = 0, maxPos = 0;\r
1470 for(i = 0; i < 6000; i++) {\r
1471 int dec = 0;\r
1472 for(j = 0; j < 3*lowLen; j++) {\r
1473 dec -= GraphBuffer[i+j];\r
1474 }\r
1475 for(; j < 3*(lowLen + highLen ); j++) {\r
1476 dec += GraphBuffer[i+j];\r
1477 }\r
1478 if(dec > max) {\r
1479 max = dec;\r
1480 maxPos = i;\r
1481 }\r
1482 }\r
1483\r
1484 // place start of bit sync marker in graph\r
1485 GraphBuffer[maxPos] = maxMark;\r
1486 GraphBuffer[maxPos+1] = minMark;\r
1487\r
1488 maxPos += j;\r
1489\r
1490 // place end of bit sync marker in graph\r
1491 GraphBuffer[maxPos] = maxMark;\r
1492 GraphBuffer[maxPos+1] = minMark;\r
1493\r
1494 PrintToScrollback("actual data bits start at sample %d", maxPos);\r
1495 PrintToScrollback("length %d/%d", highLen, lowLen);\r
1496\r
1497 BYTE bits[46];\r
1498 bits[sizeof(bits)-1] = '\0';\r
1499\r
1500 // find bit pairs and manchester decode them\r
1501 for(i = 0; i < arraylen(bits)-1; i++) {\r
1502 int dec = 0;\r
1503 for(j = 0; j < lowLen; j++) {\r
1504 dec -= GraphBuffer[maxPos+j];\r
1505 }\r
1506 for(; j < lowLen + highLen; j++) {\r
1507 dec += GraphBuffer[maxPos+j];\r
1508 }\r
1509 maxPos += j;\r
1510 // place inter bit marker in graph\r
1511 GraphBuffer[maxPos] = maxMark;\r
1512 GraphBuffer[maxPos+1] = minMark;\r
1513\r
1514 // hi and lo form a 64 bit pair\r
1515 hi = (hi<<1)|(lo>>31);\r
1516 lo = (lo<<1);\r
1517 // store decoded bit as binary (in hi/lo) and text (in bits[])\r
1518 if(dec<0) {\r
1519 bits[i] = '1';\r
1520 lo|=1;\r
1521 } else {\r
1522 bits[i] = '0';\r
1523 }\r
1524 }\r
1525 PrintToScrollback("bits: '%s'", bits);\r
1526 PrintToScrollback("hex: %08x %08x", hi, lo);\r
1527}\r
1528\r
1529// read a TI tag and return its ID\r
1530static void CmdTIRead(char *str)\r
1531{\r
1532 UsbCommand c;\r
1533 c.cmd = CMD_READ_TI_TYPE;\r
1534 SendCommand(&c, FALSE);\r
1535}\r
1536\r
1537// write new data to a r/w TI tag\r
1538static void CmdTIWrite(char *str)\r
1539{\r
1540 UsbCommand c;\r
1541 int res=0;\r
1542\r
1543 c.cmd = CMD_WRITE_TI_TYPE;\r
1544 res = sscanf(str, "0x%x 0x%x 0x%x ", &c.ext1, &c.ext2, &c.ext3);\r
1545 if (res == 2) c.ext3=0;\r
1546 if (res<2)\r
1547 PrintToScrollback("Please specify the data as two hex strings, optionally the CRC as a third");\r
1548 else\r
1549 SendCommand(&c, FALSE);\r
1550}\r
1551\r
1552static void CmdTIDemod(char *cmdline)\r
1553{\r
1554 /* MATLAB as follows:\r
1555f_s = 2000000; % sampling frequency\r
1556f_l = 123200; % low FSK tone\r
1557f_h = 134200; % high FSK tone\r
1558\r
1559T_l = 119e-6; % low bit duration\r
1560T_h = 130e-6; % high bit duration\r
1561\r
1562l = 2*pi*ones(1, floor(f_s*T_l))*(f_l/f_s);\r
1563h = 2*pi*ones(1, floor(f_s*T_h))*(f_h/f_s);\r
1564\r
1565l = sign(sin(cumsum(l)));\r
1566h = sign(sin(cumsum(h)));\r
1567 */\r
1568\r
1569// 2M*16/134.2k = 238\r
1570 static const int LowTone[] = {\r
1571 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1572 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1573 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1574 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1575 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1576 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1577 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1578 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1579 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1580 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1581 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1582 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1583 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1584 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1585 1, 1, 1, 1, 1, 1, 1, 1, -1, -1\r
1586 };\r
1587// 2M*16/123.2k = 260\r
1588 static const int HighTone[] = {\r
1589 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,\r
1590 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,\r
1591 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1592 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1593 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1594 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,\r
1595 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,\r
1596 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,\r
1597 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1598 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1599 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,\r
1600 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,\r
1601 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,\r
1602 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,\r
1603 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1604 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1605 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,\r
1606 1, 1, 1, 1, 1, 1, 1, 1\r
1607 };\r
1608 int lowLen = sizeof(LowTone)/sizeof(int);\r
1609 int highLen = sizeof(HighTone)/sizeof(int);\r
1610 int convLen = (highLen>lowLen)?highLen:lowLen;\r
1611 WORD crc;\r
1612 int i, j, TagType;\r
1613 int lowSum = 0, highSum = 0;;\r
1614 int lowTot = 0, highTot = 0;\r
1615\r
1616 for(i = 0; i < GraphTraceLen - convLen; i++) {\r
1617 lowSum = 0;\r
1618 highSum = 0;;\r
1619\r
1620 for(j = 0; j < lowLen; j++) {\r
1621 lowSum += LowTone[j]*GraphBuffer[i+j];\r
1622 }\r
1623 for(j = 0; j < highLen; j++) {\r
1624 highSum += HighTone[j]*GraphBuffer[i+j];\r
1625 }\r
1626 lowSum = abs((100*lowSum) / lowLen);\r
1627 highSum = abs((100*highSum) / highLen);\r
1628 lowSum = (lowSum<0)?-lowSum:lowSum;\r
1629 highSum = (highSum<0)?-highSum:highSum;\r
1630\r
1631 GraphBuffer[i] = (highSum << 16) | lowSum;\r
1632 }\r
1633\r
1634 for(i = 0; i < GraphTraceLen - convLen - 16; i++) {\r
1635 lowTot = 0;\r
1636 highTot = 0;\r
1637 // 16 and 15 are f_s divided by f_l and f_h, rounded\r
1638 for(j = 0; j < 16; j++) {\r
1639 lowTot += (GraphBuffer[i+j] & 0xffff);\r
1640 }\r
1641 for(j = 0; j < 15; j++) {\r
1642 highTot += (GraphBuffer[i+j] >> 16);\r
1643 }\r
1644 GraphBuffer[i] = lowTot - highTot;\r
1645 }\r
1646\r
1647 GraphTraceLen -= (convLen + 16);\r
1648\r
1649 RepaintGraphWindow();\r
1650\r
1651 // TI tag data format is 16 prebits, 8 start bits, 64 data bits,\r
1652 // 16 crc CCITT bits, 8 stop bits, 15 end bits\r
1653\r
1654 // the 16 prebits are always low\r
1655 // the 8 start and stop bits of a tag must match\r
1656 // the start/stop prebits of a ro tag are 01111110\r
1657 // the start/stop prebits of a rw tag are 11111110\r
1658 // the 15 end bits of a ro tag are all low\r
1659 // the 15 end bits of a rw tag match bits 15-1 of the data bits\r
1660\r
1661 // Okay, so now we have unsliced soft decisions;\r
1662 // find bit-sync, and then get some bits.\r
1663 // look for 17 low bits followed by 6 highs (common pattern for ro and rw tags)\r
1664 int max = 0, maxPos = 0;\r
1665 for(i = 0; i < 6000; i++) {\r
1666 int j;\r
1667 int dec = 0;\r
1668 // searching 17 consecutive lows\r
1669 for(j = 0; j < 17*lowLen; j++) {\r
1670 dec -= GraphBuffer[i+j];\r
1671 }\r
1672 // searching 7 consecutive highs\r
1673 for(; j < 17*lowLen + 6*highLen; j++) {\r
1674 dec += GraphBuffer[i+j];\r
1675 }\r
1676 if(dec > max) {\r
1677 max = dec;\r
1678 maxPos = i;\r
1679 }\r
1680 }\r
1681\r
1682 // place a marker in the buffer to visually aid location\r
1683 // of the start of sync\r
1684 GraphBuffer[maxPos] = 800;\r
1685 GraphBuffer[maxPos+1] = -800;\r
1686\r
1687 // advance pointer to start of actual data stream (after 16 pre and 8 start bits)\r
1688 maxPos += 17*lowLen;\r
1689 maxPos += 6*highLen;\r
1690\r
1691 // place a marker in the buffer to visually aid location\r
1692 // of the end of sync\r
1693 GraphBuffer[maxPos] = 800;\r
1694 GraphBuffer[maxPos+1] = -800;\r
1695\r
1696 PrintToScrollback("actual data bits start at sample %d", maxPos);\r
1697\r
1698 PrintToScrollback("length %d/%d", highLen, lowLen);\r
1699\r
1700 BYTE bits[1+64+16+8+16];\r
1701 bits[sizeof(bits)-1] = '\0';\r
1702\r
1703 DWORD shift3 = 0x7e000000, shift2 = 0, shift1 = 0, shift0 = 0;\r
1704\r
1705 for(i = 0; i < arraylen(bits)-1; i++) {\r
1706 int high = 0;\r
1707 int low = 0;\r
1708 int j;\r
1709 for(j = 0; j < lowLen; j++) {\r
1710 low -= GraphBuffer[maxPos+j];\r
1711 }\r
1712 for(j = 0; j < highLen; j++) {\r
1713 high += GraphBuffer[maxPos+j];\r
1714 }\r
1715\r
1716 if(high > low) {\r
1717 bits[i] = '1';\r
1718 maxPos += highLen;\r
1719 // bitstream arrives lsb first so shift right\r
1720 shift3 |= (1<<31);\r
1721 } else {\r
1722 bits[i] = '.';\r
1723 maxPos += lowLen;\r
1724 }\r
1725\r
1726 // 128 bit right shift register\r
1727 shift0 = (shift0>>1) | (shift1 << 31);\r
1728 shift1 = (shift1>>1) | (shift2 << 31);\r
1729 shift2 = (shift2>>1) | (shift3 << 31);\r
1730 shift3 >>= 1;\r
1731\r
1732 // place a marker in the buffer between bits to visually aid location\r
1733 GraphBuffer[maxPos] = 800;\r
1734 GraphBuffer[maxPos+1] = -800;\r
1735 }\r
1736 PrintToScrollback("Info: raw tag bits = %s", bits);\r
1737\r
1738 TagType = (shift3>>8)&0xff;\r
1739 if ( TagType != ((shift0>>16)&0xff) ) {\r
1740 PrintToScrollback("Error: start and stop bits do not match!");\r
1741 return;\r
1742 }\r
1743 else if (TagType == 0x7e) {\r
1744 PrintToScrollback("Info: Readonly TI tag detected.");\r
1745 return;\r
1746 }\r
1747 else if (TagType == 0xfe) {\r
1748 PrintToScrollback("Info: Rewriteable TI tag detected.");\r
1749\r
1750 // put 64 bit data into shift1 and shift0\r
1751 shift0 = (shift0>>24) | (shift1 << 8);\r
1752 shift1 = (shift1>>24) | (shift2 << 8);\r
1753\r
1754 // align 16 bit crc into lower half of shift2\r
1755 shift2 = ((shift2>>24) | (shift3 << 8)) & 0x0ffff;\r
1756\r
1757 // align 16 bit "end bits" or "ident" into lower half of shift3\r
1758 shift3 >>= 16;\r
1759\r
1760 // only 15 bits compare, last bit of ident is not valid\r
1761 if ( (shift3^shift0)&0x7fff ) {\r
1762 PrintToScrollback("Error: Ident mismatch!");\r
1763 }\r
1764 // WARNING the order of the bytes in which we calc crc below needs checking\r
1765 // i'm 99% sure the crc algorithm is correct, but it may need to eat the\r
1766 // bytes in reverse or something\r
1767 // calculate CRC\r
1768 crc=0;\r
1769 crc = update_crc16(crc, (shift0)&0xff);\r
1770 crc = update_crc16(crc, (shift0>>8)&0xff);\r
1771 crc = update_crc16(crc, (shift0>>16)&0xff);\r
1772 crc = update_crc16(crc, (shift0>>24)&0xff);\r
1773 crc = update_crc16(crc, (shift1)&0xff);\r
1774 crc = update_crc16(crc, (shift1>>8)&0xff);\r
1775 crc = update_crc16(crc, (shift1>>16)&0xff);\r
1776 crc = update_crc16(crc, (shift1>>24)&0xff);\r
1777 PrintToScrollback("Info: Tag data = %08X%08X", shift1, shift0);\r
1778 if (crc != (shift2&0xffff)) {\r
1779 PrintToScrollback("Error: CRC mismatch, calculated %04X, got ^04X", crc, shift2&0xffff);\r
1780 } else {\r
1781 PrintToScrollback("Info: CRC %04X is good", crc);\r
1782 }\r
1783 }\r
1784 else {\r
1785 PrintToScrollback("Unknown tag type.");\r
1786 return;\r
1787 }\r
1788}\r
1789\r
1790static void CmdNorm(char *str)\r
1791{\r
1792 int i;\r
1793 int max = INT_MIN, min = INT_MAX;\r
1794 for(i = 10; i < GraphTraceLen; i++) {\r
1795 if(GraphBuffer[i] > max) {\r
1796 max = GraphBuffer[i];\r
1797 }\r
1798 if(GraphBuffer[i] < min) {\r
1799 min = GraphBuffer[i];\r
1800 }\r
1801 }\r
1802 if(max != min) {\r
1803 for(i = 0; i < GraphTraceLen; i++) {\r
1804 GraphBuffer[i] = (GraphBuffer[i] - ((max + min)/2))*1000/\r
1805 (max - min);\r
1806 }\r
1807 }\r
1808 RepaintGraphWindow();\r
1809}\r
1810\r
1811static void CmdDec(char *str)\r
1812{\r
1813 int i;\r
1814 for(i = 0; i < (GraphTraceLen/2); i++) {\r
1815 GraphBuffer[i] = GraphBuffer[i*2];\r
1816 }\r
1817 GraphTraceLen /= 2;\r
1818 PrintToScrollback("decimated by 2");\r
1819 RepaintGraphWindow();\r
1820}\r
1821\r
1822static void CmdHpf(char *str)\r
1823{\r
1824 int i;\r
1825 int accum = 0;\r
1826 for(i = 10; i < GraphTraceLen; i++) {\r
1827 accum += GraphBuffer[i];\r
1828 }\r
1829 accum /= (GraphTraceLen - 10);\r
1830 for(i = 0; i < GraphTraceLen; i++) {\r
1831 GraphBuffer[i] -= accum;\r
1832 }\r
1833\r
1834 RepaintGraphWindow();\r
1835}\r
1836\r
1837static void CmdZerocrossings(char *str)\r
1838{\r
1839 int i;\r
1840 // Zero-crossings aren't meaningful unless the signal is zero-mean.\r
1841 CmdHpf("");\r
1842\r
1843 int sign = 1;\r
1844 int zc = 0;\r
1845 int lastZc = 0;\r
1846 for(i = 0; i < GraphTraceLen; i++) {\r
1847 if(GraphBuffer[i]*sign >= 0) {\r
1848 // No change in sign, reproduce the previous sample count.\r
1849 zc++;\r
1850 GraphBuffer[i] = lastZc;\r
1851 } else {\r
1852 // Change in sign, reset the sample count.\r
1853 sign = -sign;\r
1854 GraphBuffer[i] = lastZc;\r
1855 if(sign > 0) {\r
1856 lastZc = zc;\r
1857 zc = 0;\r
1858 }\r
1859 }\r
1860 }\r
1861\r
1862 RepaintGraphWindow();\r
1863}\r
1864\r
1865static void CmdThreshold(char *str)\r
1866{\r
1867 int i;\r
1868 int threshold = atoi(str);\r
1869\r
1870 for(i = 0; i < GraphTraceLen; i++) {\r
1871 if(GraphBuffer[i]>= threshold)\r
1872 GraphBuffer[i]=1;\r
1873 else\r
1874 GraphBuffer[i]=-1;\r
1875 }\r
1876 RepaintGraphWindow();\r
1877}\r
1878\r
1879static void CmdLtrim(char *str)\r
1880{\r
1881 int i;\r
1882 int ds = atoi(str);\r
1883\r
1884 for(i = ds; i < GraphTraceLen; i++) {\r
1885 GraphBuffer[i-ds] = GraphBuffer[i];\r
1886 }\r
1887 GraphTraceLen -= ds;\r
1888\r
1889 RepaintGraphWindow();\r
1890}\r
1891\r
1892static void CmdAutoCorr(char *str)\r
1893{\r
1894 static int CorrelBuffer[MAX_GRAPH_TRACE_LEN];\r
1895\r
1896 int window = atoi(str);\r
1897\r
1898 if(window == 0) {\r
1899 PrintToScrollback("needs a window");\r
1900 return;\r
1901 }\r
1902\r
1903 if(window >= GraphTraceLen) {\r
1904 PrintToScrollback("window must be smaller than trace (%d samples)",\r
1905 GraphTraceLen);\r
1906 return;\r
1907 }\r
1908\r
1909 PrintToScrollback("performing %d correlations", GraphTraceLen - window);\r
1910\r
1911 int i;\r
1912 for(i = 0; i < GraphTraceLen - window; i++) {\r
1913 int sum = 0;\r
1914 int j;\r
1915 for(j = 0; j < window; j++) {\r
1916 sum += (GraphBuffer[j]*GraphBuffer[i+j]) / 256;\r
1917 }\r
1918 CorrelBuffer[i] = sum;\r
1919 }\r
1920 GraphTraceLen = GraphTraceLen - window;\r
1921 memcpy(GraphBuffer, CorrelBuffer, GraphTraceLen*sizeof(int));\r
1922\r
1923 RepaintGraphWindow();\r
1924}\r
1925\r
1926static void CmdVchdemod(char *str)\r
1927{\r
1928 // Is this the entire sync pattern, or does this also include some\r
1929 // data bits that happen to be the same everywhere? That would be\r
1930 // lovely to know.\r
1931 static const int SyncPattern[] = {\r
1932 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
1933 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1934 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
1935 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1936 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
1937 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1938 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
1939 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1940 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\r
1941 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
1942 };\r
1943\r
1944 // So first, we correlate for the sync pattern, and mark that.\r
1945 int bestCorrel = 0, bestPos = 0;\r
1946 int i;\r
1947 // It does us no good to find the sync pattern, with fewer than\r
1948 // 2048 samples after it...\r
1949 for(i = 0; i < (GraphTraceLen-2048); i++) {\r
1950 int sum = 0;\r
1951 int j;\r
1952 for(j = 0; j < arraylen(SyncPattern); j++) {\r
1953 sum += GraphBuffer[i+j]*SyncPattern[j];\r
1954 }\r
1955 if(sum > bestCorrel) {\r
1956 bestCorrel = sum;\r
1957 bestPos = i;\r
1958 }\r
1959 }\r
1960 PrintToScrollback("best sync at %d [metric %d]", bestPos, bestCorrel);\r
1961\r
1962 char bits[257];\r
1963 bits[256] = '\0';\r
1964\r
1965 int worst = INT_MAX;\r
1966 int worstPos;\r
1967\r
1968 for(i = 0; i < 2048; i += 8) {\r
1969 int sum = 0;\r
1970 int j;\r
1971 for(j = 0; j < 8; j++) {\r
1972 sum += GraphBuffer[bestPos+i+j];\r
1973 }\r
1974 if(sum < 0) {\r
1975 bits[i/8] = '.';\r
1976 } else {\r
1977 bits[i/8] = '1';\r
1978 }\r
1979 if(abs(sum) < worst) {\r
1980 worst = abs(sum);\r
1981 worstPos = i;\r
1982 }\r
1983 }\r
1984 PrintToScrollback("bits:");\r
1985 PrintToScrollback("%s", bits);\r
1986 PrintToScrollback("worst metric: %d at pos %d", worst, worstPos);\r
1987\r
1988 if(strcmp(str, "clone")==0) {\r
1989 GraphTraceLen = 0;\r
1990 char *s;\r
1991 for(s = bits; *s; s++) {\r
1992 int j;\r
1993 for(j = 0; j < 16; j++) {\r
1994 GraphBuffer[GraphTraceLen++] = (*s == '1') ? 1 : 0;\r
1995 }\r
1996 }\r
1997 RepaintGraphWindow();\r
1998 }\r
1999}\r
2000\r
2001static void CmdIndalademod(char *str)\r
2002{\r
2003 // Usage: recover 64bit UID by default, specify "224" as arg to recover a 224bit UID\r
2004\r
2005 int state = -1;\r
2006 int count = 0;\r
2007 int i, j;\r
2008 // worst case with GraphTraceLen=64000 is < 4096\r
2009 // under normal conditions it's < 2048\r
2010 BYTE rawbits[4096];\r
2011 int rawbit = 0;\r
2012 int worst = 0, worstPos = 0;\r
2013 PrintToScrollback("Expecting a bit less than %d raw bits", GraphTraceLen/32);\r
2014 for(i = 0; i < GraphTraceLen-1; i += 2) {\r
2015 count+=1;\r
2016 if((GraphBuffer[i] > GraphBuffer[i + 1]) && (state != 1)) {\r
2017 if (state == 0) {\r
2018 for(j = 0; j < count - 8; j += 16) {\r
2019 rawbits[rawbit++] = 0;\r
2020 }\r
2021 if ((abs(count - j)) > worst) {\r
2022 worst = abs(count - j);\r
2023 worstPos = i;\r
2024 }\r
2025 }\r
2026 state = 1;\r
2027 count=0;\r
2028 } else if((GraphBuffer[i] < GraphBuffer[i + 1]) && (state != 0)) {\r
2029 if (state == 1) {\r
2030 for(j = 0; j < count - 8; j += 16) {\r
2031 rawbits[rawbit++] = 1;\r
2032 }\r
2033 if ((abs(count - j)) > worst) {\r
2034 worst = abs(count - j);\r
2035 worstPos = i;\r
2036 }\r
2037 }\r
2038 state = 0;\r
2039 count=0;\r
2040 }\r
2041 }\r
2042 PrintToScrollback("Recovered %d raw bits", rawbit);\r
2043 PrintToScrollback("worst metric (0=best..7=worst): %d at pos %d", worst, worstPos);\r
2044\r
2045 // Finding the start of a UID\r
2046 int uidlen, long_wait;\r
2047 if(strcmp(str, "224") == 0) {\r
2048 uidlen=224;\r
2049 long_wait=30;\r
2050 } else {\r
2051 uidlen=64;\r
2052 long_wait=29;\r
2053 }\r
2054 int start;\r
2055 int first = 0;\r
2056 for(start = 0; start <= rawbit - uidlen; start++) {\r
2057 first = rawbits[start];\r
2058 for(i = start; i < start + long_wait; i++) {\r
2059 if(rawbits[i] != first) {\r
2060 break;\r
2061 }\r
2062 }\r
2063 if(i == (start + long_wait)) {\r
2064 break;\r
2065 }\r
2066 }\r
2067 if(start == rawbit - uidlen + 1) {\r
2068 PrintToScrollback("nothing to wait for");\r
2069 return;\r
2070 }\r
2071\r
2072 // Inverting signal if needed\r
2073 if(first == 1) {\r
2074 for(i = start; i < rawbit; i++) {\r
2075 rawbits[i] = !rawbits[i];\r
2076 }\r
2077 }\r
2078\r
2079 // Dumping UID\r
2080 BYTE bits[224];\r
2081 char showbits[225];\r
2082 showbits[uidlen]='\0';\r
2083 int bit;\r
2084 i = start;\r
2085 int times = 0;\r
2086 if(uidlen > rawbit) {\r
2087 PrintToScrollback("Warning: not enough raw bits to get a full UID");\r
2088 for(bit = 0; bit < rawbit; bit++) {\r
2089 bits[bit] = rawbits[i++];\r
2090 // As we cannot know the parity, let's use "." and "/"\r
2091 showbits[bit] = '.' + bits[bit];\r
2092 }\r
2093 showbits[bit+1]='\0';\r
2094 PrintToScrollback("Partial UID=%s", showbits);\r
2095 return;\r
2096 } else {\r
2097 for(bit = 0; bit < uidlen; bit++) {\r
2098 bits[bit] = rawbits[i++];\r
2099 showbits[bit] = '0' + bits[bit];\r
2100 }\r
2101 times = 1;\r
2102 }\r
2103 PrintToScrollback("UID=%s", showbits);\r
2104\r
2105 // Checking UID against next occurences\r
2106 for(; i + uidlen <= rawbit;) {\r
2107 int failed = 0;\r
2108 for(bit = 0; bit < uidlen; bit++) {\r
2109 if(bits[bit] != rawbits[i++]) {\r
2110 failed = 1;\r
2111 break;\r
2112 }\r
2113 }\r
2114 if (failed == 1) {\r
2115 break;\r
2116 }\r
2117 times += 1;\r
2118 }\r
2119 PrintToScrollback("Occurences: %d (expected %d)", times, (rawbit - start) / uidlen);\r
2120\r
2121 // Remodulating for tag cloning\r
2122 GraphTraceLen = 32*uidlen;\r
2123 i = 0;\r
2124 int phase = 0;\r
2125 for(bit = 0; bit < uidlen; bit++) {\r
2126 if(bits[bit] == 0) {\r
2127 phase = 0;\r
2128 } else {\r
2129 phase = 1;\r
2130 }\r
2131 int j;\r
2132 for(j = 0; j < 32; j++) {\r
2133 GraphBuffer[i++] = phase;\r
2134 phase = !phase;\r
2135 }\r
2136 }\r
2137\r
2138 RepaintGraphWindow();\r
2139}\r
2140\r
2141static void CmdFlexdemod(char *str)\r
2142{\r
2143 int i;\r
2144 for(i = 0; i < GraphTraceLen; i++) {\r
2145 if(GraphBuffer[i] < 0) {\r
2146 GraphBuffer[i] = -1;\r
2147 } else {\r
2148 GraphBuffer[i] = 1;\r
2149 }\r
2150 }\r
2151\r
2152#define LONG_WAIT 100\r
2153 int start;\r
2154 for(start = 0; start < GraphTraceLen - LONG_WAIT; start++) {\r
2155 int first = GraphBuffer[start];\r
2156 for(i = start; i < start + LONG_WAIT; i++) {\r
2157 if(GraphBuffer[i] != first) {\r
2158 break;\r
2159 }\r
2160 }\r
2161 if(i == (start + LONG_WAIT)) {\r
2162 break;\r
2163 }\r
2164 }\r
2165 if(start == GraphTraceLen - LONG_WAIT) {\r
2166 PrintToScrollback("nothing to wait for");\r
2167 return;\r
2168 }\r
2169\r
2170 GraphBuffer[start] = 2;\r
2171 GraphBuffer[start+1] = -2;\r
2172\r
2173 BYTE bits[64];\r
2174\r
2175 int bit;\r
2176 i = start;\r
2177 for(bit = 0; bit < 64; bit++) {\r
2178 int j;\r
2179 int sum = 0;\r
2180 for(j = 0; j < 16; j++) {\r
2181 sum += GraphBuffer[i++];\r
2182 }\r
2183 if(sum > 0) {\r
2184 bits[bit] = 1;\r
2185 } else {\r
2186 bits[bit] = 0;\r
2187 }\r
2188 PrintToScrollback("bit %d sum %d", bit, sum);\r
2189 }\r
2190\r
2191 for(bit = 0; bit < 64; bit++) {\r
2192 int j;\r
2193 int sum = 0;\r
2194 for(j = 0; j < 16; j++) {\r
2195 sum += GraphBuffer[i++];\r
2196 }\r
2197 if(sum > 0 && bits[bit] != 1) {\r
2198 PrintToScrollback("oops1 at %d", bit);\r
2199 }\r
2200 if(sum < 0 && bits[bit] != 0) {\r
2201 PrintToScrollback("oops2 at %d", bit);\r
2202 }\r
2203 }\r
2204\r
2205 GraphTraceLen = 32*64;\r
2206 i = 0;\r
2207 int phase = 0;\r
2208 for(bit = 0; bit < 64; bit++) {\r
2209 if(bits[bit] == 0) {\r
2210 phase = 0;\r
2211 } else {\r
2212 phase = 1;\r
2213 }\r
2214 int j;\r
2215 for(j = 0; j < 32; j++) {\r
2216 GraphBuffer[i++] = phase;\r
2217 phase = !phase;\r
2218 }\r
2219 }\r
2220\r
2221 RepaintGraphWindow();\r
2222}\r
2223\r
2224/*\r
2225 * Generic command to demodulate ASK.\r
2226 *\r
2227 * Argument is convention: positive or negative (High mod means zero\r
2228 * or high mod means one)\r
2229 *\r
2230 * Updates the Graph trace with 0/1 values\r
2231 *\r
2232 * Arguments:\r
2233 * c : 0 or 1\r
2234 */\r
2235\r
2236static void Cmdaskdemod(char *str) {\r
2237 int i;\r
2238 int c, high = 0, low = 0;\r
2239\r
2240 // TODO: complain if we do not give 2 arguments here !\r
2241 sscanf(str, "%i", &c);\r
2242\r
2243 /* Detect high and lows and clock */\r
2244 for (i = 0; i < GraphTraceLen; i++)\r
2245 {\r
2246 if (GraphBuffer[i] > high)\r
2247 high = GraphBuffer[i];\r
2248 else if (GraphBuffer[i] < low)\r
2249 low = GraphBuffer[i];\r
2250 }\r
2251\r
2252 if (GraphBuffer[0] > 0) {\r
2253 GraphBuffer[0] = 1-c;\r
2254 } else {\r
2255 GraphBuffer[0] = c;\r
2256 }\r
2257 for(i=1;i<GraphTraceLen;i++) {\r
2258 /* Transitions are detected at each peak\r
2259 * Transitions are either:\r
2260 * - we're low: transition if we hit a high\r
2261 * - we're high: transition if we hit a low\r
2262 * (we need to do it this way because some tags keep high or\r
2263 * low for long periods, others just reach the peak and go\r
2264 * down)\r
2265 */\r
2266 if ((GraphBuffer[i]==high) && (GraphBuffer[i-1] == c)) {\r
2267 GraphBuffer[i]=1-c;\r
2268 } else if ((GraphBuffer[i]==low) && (GraphBuffer[i-1] == (1-c))){\r
2269 GraphBuffer[i] = c;\r
2270 } else {\r
2271 /* No transition */\r
2272 GraphBuffer[i] = GraphBuffer[i-1];\r
2273 }\r
2274 }\r
2275 RepaintGraphWindow();\r
2276}\r
2277\r
2278/* Print our clock rate */\r
2279static void Cmddetectclockrate(char *str)\r
2280{\r
2281 int clock = detectclock(0);\r
2282 PrintToScrollback("Auto-detected clock rate: %d", clock);\r
2283}\r
2284\r
2285/*\r
2286 * Detect clock rate\r
2287 */\r
2288int detectclock(int peak)\r
2289{\r
2290 int i;\r
2291 int clock = 0xFFFF;\r
2292 int lastpeak = 0;\r
2293\r
2294 /* Detect peak if we don't have one */\r
2295 if (!peak)\r
2296 for (i = 0; i < GraphTraceLen; i++)\r
2297 if (GraphBuffer[i] > peak)\r
2298 peak = GraphBuffer[i];\r
2299\r
2300 for (i = 1; i < GraphTraceLen; i++)\r
2301 {\r
2302 /* If this is the beginning of a peak */\r
2303 if (GraphBuffer[i-1] != GraphBuffer[i] && GraphBuffer[i] == peak)\r
2304 {\r
2305 /* Find lowest difference between peaks */\r
2306 if (lastpeak && i - lastpeak < clock)\r
2307 {\r
2308 clock = i - lastpeak;\r
2309 }\r
2310 lastpeak = i;\r
2311 }\r
2312 }\r
2313\r
2314 return clock;\r
2315}\r
2316\r
2317/* Get or auto-detect clock rate */\r
2318int GetClock(char *str, int peak)\r
2319{\r
2320 int clock;\r
2321\r
2322 sscanf(str, "%i", &clock);\r
2323 if (!strcmp(str, ""))\r
2324 clock = 0;\r
2325\r
2326 /* Auto-detect clock */\r
2327 if (!clock)\r
2328 {\r
2329 clock = detectclock(peak);\r
2330\r
2331 /* Only print this message if we're not looping something */\r
2332 if (!go)\r
2333 PrintToScrollback("Auto-detected clock rate: %d", clock);\r
2334 }\r
2335\r
2336 return clock;\r
2337}\r
2338\r
2339/*\r
2340 * Convert to a bitstream\r
2341 */\r
2342static void Cmdbitstream(char *str) {\r
2343 int i, j;\r
2344 int bit;\r
2345 int gtl;\r
2346 int clock;\r
2347 int low = 0;\r
2348 int high = 0;\r
2349 int hithigh, hitlow, first;\r
2350\r
2351 /* Detect high and lows and clock */\r
2352 for (i = 0; i < GraphTraceLen; i++)\r
2353 {\r
2354 if (GraphBuffer[i] > high)\r
2355 high = GraphBuffer[i];\r
2356 else if (GraphBuffer[i] < low)\r
2357 low = GraphBuffer[i];\r
2358 }\r
2359\r
2360 /* Get our clock */\r
2361 clock = GetClock(str, high);\r
2362\r
2363 gtl = CmdClearGraph(0);\r
2364\r
2365 bit = 0;\r
2366 for (i = 0; i < (int)(gtl / clock); i++)\r
2367 {\r
2368 hithigh = 0;\r
2369 hitlow = 0;\r
2370 first = 1;\r
2371\r
2372 /* Find out if we hit both high and low peaks */\r
2373 for (j = 0; j < clock; j++)\r
2374 {\r
2375 if (GraphBuffer[(i * clock) + j] == high)\r
2376 hithigh = 1;\r
2377 else if (GraphBuffer[(i * clock) + j] == low)\r
2378 hitlow = 1;\r
2379\r
2380 /* it doesn't count if it's the first part of our read\r
2381 because it's really just trailing from the last sequence */\r
2382 if (first && (hithigh || hitlow))\r
2383 hithigh = hitlow = 0;\r
2384 else\r
2385 first = 0;\r
2386\r
2387 if (hithigh && hitlow)\r
2388 break;\r
2389 }\r
2390\r
2391 /* If we didn't hit both high and low peaks, we had a bit transition */\r
2392 if (!hithigh || !hitlow)\r
2393 bit ^= 1;\r
2394\r
2395 CmdAppendGraph(0, clock, bit);\r
2396// for (j = 0; j < (int)(clock/2); j++)\r
2397// GraphBuffer[(i * clock) + j] = bit ^ 1;\r
2398// for (j = (int)(clock/2); j < clock; j++)\r
2399// GraphBuffer[(i * clock) + j] = bit;\r
2400 }\r
2401\r
2402 RepaintGraphWindow();\r
2403}\r
2404\r
2405/* Modulate our data into manchester */\r
2406static void Cmdmanchestermod(char *str)\r
2407{\r
2408 int i, j;\r
2409 int clock;\r
2410 int bit, lastbit, wave;\r
2411\r
2412 /* Get our clock */\r
2413 clock = GetClock(str, 0);\r
2414\r
2415 wave = 0;\r
2416 lastbit = 1;\r
2417 for (i = 0; i < (int)(GraphTraceLen / clock); i++)\r
2418 {\r
2419 bit = GraphBuffer[i * clock] ^ 1;\r
2420\r
2421 for (j = 0; j < (int)(clock/2); j++)\r
2422 GraphBuffer[(i * clock) + j] = bit ^ lastbit ^ wave;\r
2423 for (j = (int)(clock/2); j < clock; j++)\r
2424 GraphBuffer[(i * clock) + j] = bit ^ lastbit ^ wave ^ 1;\r
2425\r
2426 /* Keep track of how we start our wave and if we changed or not this time */\r
2427 wave ^= bit ^ lastbit;\r
2428 lastbit = bit;\r
2429 }\r
2430\r
2431 RepaintGraphWindow();\r
2432}\r
2433\r
2434/*\r
2435 * Manchester demodulate a bitstream. The bitstream needs to be already in\r
2436 * the GraphBuffer as 0 and 1 values\r
2437 *\r
2438 * Give the clock rate as argument in order to help the sync - the algorithm\r
2439 * resyncs at each pulse anyway.\r
2440 *\r
2441 * Not optimized by any means, this is the 1st time I'm writing this type of\r
2442 * routine, feel free to improve...\r
2443 *\r
2444 * 1st argument: clock rate (as number of samples per clock rate)\r
2445 * Typical values can be 64, 32, 128...\r
2446 */\r
2447static void Cmdmanchesterdemod(char *str) {\r
2448 int i, j, invert= 0;\r
2449 int bit;\r
2450 int clock;\r
2451 int lastval;\r
2452 int low = 0;\r
2453 int high = 0;\r
2454 int hithigh, hitlow, first;\r
2455 int lc = 0;\r
2456 int bitidx = 0;\r
2457 int bit2idx = 0;\r
2458 int warnings = 0;\r
2459\r
2460 /* check if we're inverting output */\r
2461 if(*str == 'i')\r
2462 {\r
2463 PrintToScrollback("Inverting output");\r
2464 invert= 1;\r
2465 do\r
2466 ++str;\r
2467 while(*str == ' '); // in case a 2nd argument was given\r
2468 }\r
2469\r
2470 /* Holds the decoded bitstream: each clock period contains 2 bits */\r
2471 /* later simplified to 1 bit after manchester decoding. */\r
2472 /* Add 10 bits to allow for noisy / uncertain traces without aborting */\r
2473 /* int BitStream[GraphTraceLen*2/clock+10]; */\r
2474\r
2475 /* But it does not work if compiling on WIndows: therefore we just allocate a */\r
2476 /* large array */\r
2477 int BitStream[MAX_GRAPH_TRACE_LEN];\r
2478\r
2479 /* Detect high and lows */\r
2480 for (i = 0; i < GraphTraceLen; i++)\r
2481 {\r
2482 if (GraphBuffer[i] > high)\r
2483 high = GraphBuffer[i];\r
2484 else if (GraphBuffer[i] < low)\r
2485 low = GraphBuffer[i];\r
2486 }\r
2487\r
2488 /* Get our clock */\r
2489 clock = GetClock(str, high);\r
2490\r
2491 int tolerance = clock/4;\r
2492\r
2493 /* Detect first transition */\r
2494 /* Lo-Hi (arbitrary) */\r
2495 for (i = 0; i < GraphTraceLen; i++)\r
2496 {\r
2497 if (GraphBuffer[i] == low)\r
2498 {\r
2499 lastval = i;\r
2500 break;\r
2501 }\r
2502 }\r
2503\r
2504 /* If we're not working with 1/0s, demod based off clock */\r
2505 if (high != 1)\r
2506 {\r
2507 bit = 0; /* We assume the 1st bit is zero, it may not be\r
2508 * the case: this routine (I think) has an init problem.\r
2509 * Ed.\r
2510 */\r
2511 for (; i < (int)(GraphTraceLen / clock); i++)\r
2512 {\r
2513 hithigh = 0;\r
2514 hitlow = 0;\r
2515 first = 1;\r
2516\r
2517 /* Find out if we hit both high and low peaks */\r
2518 for (j = 0; j < clock; j++)\r
2519 {\r
2520 if (GraphBuffer[(i * clock) + j] == high)\r
2521 hithigh = 1;\r
2522 else if (GraphBuffer[(i * clock) + j] == low)\r
2523 hitlow = 1;\r
2524\r
2525 /* it doesn't count if it's the first part of our read\r
2526 because it's really just trailing from the last sequence */\r
2527 if (first && (hithigh || hitlow))\r
2528 hithigh = hitlow = 0;\r
2529 else\r
2530 first = 0;\r
2531\r
2532 if (hithigh && hitlow)\r
2533 break;\r
2534 }\r
2535\r
2536 /* If we didn't hit both high and low peaks, we had a bit transition */\r
2537 if (!hithigh || !hitlow)\r
2538 bit ^= 1;\r
2539\r
2540 BitStream[bit2idx++] = bit ^ invert;\r
2541 }\r
2542 }\r
2543\r
2544 /* standard 1/0 bitstream */\r
2545 else\r
2546 {\r
2547\r
2548 /* Then detect duration between 2 successive transitions */\r
2549 for (bitidx = 1; i < GraphTraceLen; i++)\r
2550 {\r
2551 if (GraphBuffer[i-1] != GraphBuffer[i])\r
2552 {\r
2553 lc = i-lastval;\r
2554 lastval = i;\r
2555\r
2556 // Error check: if bitidx becomes too large, we do not\r
2557 // have a Manchester encoded bitstream or the clock is really\r
2558 // wrong!\r
2559 if (bitidx > (GraphTraceLen*2/clock+8) ) {\r
2560 PrintToScrollback("Error: the clock you gave is probably wrong, aborting.");\r
2561 return;\r
2562 }\r
2563 // Then switch depending on lc length:\r
2564 // Tolerance is 1/4 of clock rate (arbitrary)\r
2565 if (abs(lc-clock/2) < tolerance) {\r
2566 // Short pulse : either "1" or "0"\r
2567 BitStream[bitidx++]=GraphBuffer[i-1];\r
2568 } else if (abs(lc-clock) < tolerance) {\r
2569 // Long pulse: either "11" or "00"\r
2570 BitStream[bitidx++]=GraphBuffer[i-1];\r
2571 BitStream[bitidx++]=GraphBuffer[i-1];\r
2572 } else {\r
2573 // Error\r
2574 warnings++;\r
2575 PrintToScrollback("Warning: Manchester decode error for pulse width detection.");\r
2576 PrintToScrollback("(too many of those messages mean either the stream is not Manchester encoded, or clock is wrong)");\r
2577\r
2578 if (warnings > 100)\r
2579 {\r
2580 PrintToScrollback("Error: too many detection errors, aborting.");\r
2581 return;\r
2582 }\r
2583 }\r
2584 }\r
2585 }\r
2586\r
2587 // At this stage, we now have a bitstream of "01" ("1") or "10" ("0"), parse it into final decoded bitstream\r
2588 // Actually, we overwrite BitStream with the new decoded bitstream, we just need to be careful\r
2589 // to stop output at the final bitidx2 value, not bitidx\r
2590 for (i = 0; i < bitidx; i += 2) {\r
2591 if ((BitStream[i] == 0) && (BitStream[i+1] == 1)) {\r
2592 BitStream[bit2idx++] = 1 ^ invert;\r
2593 } else if ((BitStream[i] == 1) && (BitStream[i+1] == 0)) {\r
2594 BitStream[bit2idx++] = 0 ^ invert;\r
2595 } else {\r
2596 // We cannot end up in this state, this means we are unsynchronized,\r
2597 // move up 1 bit:\r
2598 i++;\r
2599 warnings++;\r
2600 PrintToScrollback("Unsynchronized, resync...");\r
2601 PrintToScrollback("(too many of those messages mean the stream is not Manchester encoded)");\r
2602\r
2603 if (warnings > 100)\r
2604 {\r
2605 PrintToScrollback("Error: too many decode errors, aborting.");\r
2606 return;\r
2607 }\r
2608 }\r
2609 }\r
2610 }\r
2611\r
2612 PrintToScrollback("Manchester decoded bitstream");\r
2613 // Now output the bitstream to the scrollback by line of 16 bits\r
2614 for (i = 0; i < (bit2idx-16); i+=16) {\r
2615 PrintToScrollback("%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",\r
2616 BitStream[i],\r
2617 BitStream[i+1],\r
2618 BitStream[i+2],\r
2619 BitStream[i+3],\r
2620 BitStream[i+4],\r
2621 BitStream[i+5],\r
2622 BitStream[i+6],\r
2623 BitStream[i+7],\r
2624 BitStream[i+8],\r
2625 BitStream[i+9],\r
2626 BitStream[i+10],\r
2627 BitStream[i+11],\r
2628 BitStream[i+12],\r
2629 BitStream[i+13],\r
2630 BitStream[i+14],\r
2631 BitStream[i+15]);\r
2632 }\r
2633}\r
2634\r
2635/*\r
2636 * Usage ???\r
2637 */\r
2638static void CmdHiddemod(char *str)\r
2639{\r
2640 if(GraphTraceLen < 4800) {\r
2641 PrintToScrollback("too short; need at least 4800 samples");\r
2642 return;\r
2643 }\r
2644\r
2645 GraphTraceLen = 4800;\r
2646 int i;\r
2647 for(i = 0; i < GraphTraceLen; i++) {\r
2648 if(GraphBuffer[i] < 0) {\r
2649 GraphBuffer[i] = 0;\r
2650 } else {\r
2651 GraphBuffer[i] = 1;\r
2652 }\r
2653 }\r
2654 RepaintGraphWindow();\r
2655}\r
2656\r
2657static void CmdPlot(char *str)\r
2658{\r
2659 ShowGraphWindow();\r
2660}\r
2661\r
2662static void CmdGrid(char *str)\r
2663{\r
2664 sscanf(str, "%i %i", &PlotGridX, &PlotGridY);\r
2665 RepaintGraphWindow();\r
2666}\r
2667\r
2668static void CmdHide(char *str)\r
2669{\r
2670 HideGraphWindow();\r
2671}\r
2672\r
2673static void CmdScale(char *str)\r
2674{\r
2675 CursorScaleFactor = atoi(str);\r
2676 if(CursorScaleFactor == 0) {\r
2677 PrintToScrollback("bad, can't have zero scale");\r
2678 CursorScaleFactor = 1;\r
2679 }\r
2680 RepaintGraphWindow();\r
2681}\r
2682\r
2683static void CmdSave(char *str)\r
2684{\r
2685 FILE *f = fopen(str, "w");\r
2686 if(!f) {\r
2687 PrintToScrollback("couldn't open '%s'", str);\r
2688 return;\r
2689 }\r
2690 int i;\r
2691 for(i = 0; i < GraphTraceLen; i++) {\r
2692 fprintf(f, "%d\n", GraphBuffer[i]);\r
2693 }\r
2694 fclose(f);\r
2695 PrintToScrollback("saved to '%s'", str);\r
2696}\r
2697\r
2698static void CmdLoad(char *str)\r
2699{\r
2700 FILE *f = fopen(str, "r");\r
2701 if(!f) {\r
2702 PrintToScrollback("couldn't open '%s'", str);\r
2703 return;\r
2704 }\r
2705\r
2706 GraphTraceLen = 0;\r
2707 char line[80];\r
2708 while(fgets(line, sizeof(line), f)) {\r
2709 GraphBuffer[GraphTraceLen] = atoi(line);\r
2710 GraphTraceLen++;\r
2711 }\r
2712 fclose(f);\r
2713 PrintToScrollback("loaded %d samples", GraphTraceLen);\r
2714 RepaintGraphWindow();\r
2715}\r
2716\r
2717static void CmdHIDsimTAG(char *str)\r
2718{\r
2719 unsigned int hi=0, lo=0;\r
2720 int n=0, i=0;\r
2721 UsbCommand c;\r
2722\r
2723 while (sscanf(&str[i++], "%1x", &n ) == 1) {\r
2724 hi=(hi<<4)|(lo>>28);\r
2725 lo=(lo<<4)|(n&0xf);\r
2726 }\r
2727\r
2728 PrintToScrollback("Emulating tag with ID %x%16x", hi, lo);\r
2729\r
2730 c.cmd = CMD_HID_SIM_TAG;\r
2731 c.ext1 = hi;\r
2732 c.ext2 = lo;\r
2733 SendCommand(&c, FALSE);\r
2734}\r
2735\r
2736static void CmdReadmem(char *str)\r
2737{\r
2738 UsbCommand c;\r
2739 c.cmd = CMD_READ_MEM;\r
2740 c.ext1 = atoi(str);\r
2741 SendCommand(&c, FALSE);\r
2742}\r
2743\r
2744static void CmdVersion(char *str)\r
2745{\r
2746 UsbCommand c;\r
2747 c.cmd = CMD_VERSION;\r
2748 SendCommand(&c, FALSE);\r
2749}\r
2750\r
2751static void CmdLcdReset(char *str)\r
2752{\r
2753 UsbCommand c;\r
2754 c.cmd = CMD_LCD_RESET;\r
2755 c.ext1 = atoi(str);\r
2756 SendCommand(&c, FALSE);\r
2757}\r
2758\r
2759static void CmdLcd(char *str)\r
2760{\r
2761 int i, j;\r
2762 UsbCommand c;\r
2763 c.cmd = CMD_LCD;\r
2764 sscanf(str, "%x %d", &i, &j);\r
2765 while (j--) {\r
2766 c.ext1 = i&0x1ff;\r
2767 SendCommand(&c, FALSE);\r
2768 }\r
2769}\r
2770\r
2771/*\r
2772 * Sets the divisor for LF frequency clock: lets the user choose any LF frequency below\r
2773 * 600kHz.\r
2774 */\r
2775static void CmdSetDivisor(char *str)\r
2776{\r
2777 UsbCommand c;\r
2778 c.cmd = CMD_SET_LF_DIVISOR;\r
2779 c.ext1 = atoi(str);\r
2780 if (( c.ext1<0) || (c.ext1>255)) {\r
2781 PrintToScrollback("divisor must be between 19 and 255");\r
2782 } else {\r
2783 SendCommand(&c, FALSE);\r
2784 PrintToScrollback("Divisor set, expected freq=%dHz", 12000000/(c.ext1+1));\r
2785 }\r
2786}\r
2787\r
2788typedef void HandlerFunction(char *cmdline);\r
2789\r
2790/* in alphabetic order */\r
2791static struct {\r
2792 char *name;\r
2793 HandlerFunction *handler;\r
2794 int offline; // 1 if the command can be used when in offline mode\r
2795 char *docString;\r
2796} CommandTable[] = {\r
2797 {"askdemod", Cmdaskdemod, 1, "<samples per bit> <0|1> -- Attempt to demodulate simple ASK tags"},\r
2798 {"autocorr", CmdAutoCorr, 1, "<window length> -- Autocorrelation over window"},\r
2799 {"bitsamples", CmdBitsamples, 0, "Get raw samples as bitstring"},\r
2800 {"bitstream", Cmdbitstream, 1, "[clock rate] -- Convert waveform into a bitstream"},\r
2801 {"buffclear", CmdBuffClear, 1, "Clear sample buffer and graph window"},\r
2802 {"dec", CmdDec, 1, "Decimate samples"},\r
2803 {"detectclock", Cmddetectclockrate, 1, "Detect clock rate"},\r
2804 {"detectreader", CmdDetectReader, 0, "['l'|'h'] -- Detect external reader field (option 'l' or 'h' to limit to LF or HF)"},\r
2805 {"em410xsim", CmdEM410xsim, 1, "<UID> -- Simulate EM410x tag"},\r
2806 {"em410xread", CmdEM410xread, 1, "[clock rate] -- Extract ID from EM410x tag"},\r
2807 {"em410xwatch", CmdEM410xwatch, 0, "Watches for EM410x tags"},\r
2808 {"em4x50read", CmdEM4x50read, 1, "Extract data from EM4x50 tag"},\r
2809 {"exit", CmdQuit, 1, "Exit program"},\r
2810 {"flexdemod", CmdFlexdemod, 1, "Demodulate samples for FlexPass"},\r
2811 {"fpgaoff", CmdFPGAOff, 0, "Set FPGA off"},\r
2812 {"fskdemod", CmdFSKdemod, 1, "Demodulate graph window as a HID FSK"},\r
2813 {"grid", CmdGrid, 1, "<x> <y> -- overlay grid on graph window, use zero value to turn off either"},\r
2814 {"hexsamples", CmdHexsamples, 0, "<blocks> -- Dump big buffer as hex bytes"},\r
2815 {"hi14alist", CmdHi14alist, 0, "List ISO 14443a history"},\r
2816 {"hi14areader", CmdHi14areader, 0, "Act like an ISO14443 Type A reader"},\r
2817 {"hi14asim", CmdHi14asim, 0, "<UID> -- Fake ISO 14443a tag"},\r
2818 {"hi14asnoop", CmdHi14asnoop, 0, "Eavesdrop ISO 14443 Type A"},\r
2819 {"hi14bdemod", CmdHi14bdemod, 1, "Demodulate ISO14443 Type B from tag"},\r
2820 {"hi14list", CmdHi14list, 0, "List ISO 14443 history"},\r
2821 {"hi14read", CmdHi14read, 0, "Read HF tag (ISO 14443)"},\r
2822 {"hi14sim", CmdHi14sim, 0, "Fake ISO 14443 tag"},\r
2823 {"hi14snoop", CmdHi14snoop, 0, "Eavesdrop ISO 14443"},\r
2824 {"hi15demod", CmdHi15demod, 1, "Demodulate ISO15693 from tag"},\r
2825 {"hi15read", CmdHi15read, 0, "Read HF tag (ISO 15693)"},\r
2826 {"hi15reader", CmdHi15reader, 0, "Act like an ISO15693 reader"},\r
2827 {"hi15sim", CmdHi15tag, 0, "Fake an ISO15693 tag"},\r
2828 {"hiddemod", CmdHiddemod, 1, "Demodulate HID Prox Card II (not optimal)"},\r
2829 {"hide", CmdHide, 1, "Hide graph window"},\r
2830 {"hidfskdemod", CmdHIDdemodFSK, 0, "Realtime HID FSK demodulator"},\r
2831 {"hidsimtag", CmdHIDsimTAG, 0, "<ID> -- HID tag simulator"},\r
2832 {"higet", CmdHi14read_sim, 0, "<samples> -- Get samples HF, 'analog'"},\r
2833 {"hisamples", CmdHisamples, 0, "Get raw samples for HF tag"},\r
2834 {"hisampless", CmdHisampless, 0, "<samples> -- Get signed raw samples, HF tag"},\r
2835 {"hisamplest", CmdHi14readt, 0, "Get samples HF, for testing"},\r
2836 {"hisimlisten", CmdHisimlisten, 0, "Get HF samples as fake tag"},\r
2837 {"hpf", CmdHpf, 1, "Remove DC offset from trace"},\r
2838 {"indalademod", CmdIndalademod, 0, "['224'] -- Demodulate samples for Indala 64 bit UID (option '224' for 224 bit)"},\r
2839 {"lcd", CmdLcd, 0, "<HEX command> <count> -- Send command/data to LCD"},\r
2840 {"lcdreset", CmdLcdReset, 0, "Hardware reset LCD"},\r
2841 {"load", CmdLoad, 1, "<filename> -- Load trace (to graph window"},\r
2842 {"locomread", CmdLoCommandRead, 0, "<off period> <'0' period> <'1' period> <command> ['h'] -- Modulate LF reader field to send command before read (all periods in microseconds) (option 'h' for 134)"},\r
2843 {"loread", CmdLoread, 0, "['h'] -- Read 125/134 kHz LF ID-only tag (option 'h' for 134)"},\r
2844 {"losamples", CmdLosamples, 0, "[128 - 16000] -- Get raw samples for LF tag"},\r
2845 {"losim", CmdLosim, 0, "Simulate LF tag"},\r
2846 {"losimbidir", CmdLosimBidir, 0, "Simulate LF tag (with bidirectional data transmission between reader and tag)"},\r
2847 {"ltrim", CmdLtrim, 1, "<samples> -- Trim samples from left of trace"},\r
2848 {"mandemod", Cmdmanchesterdemod, 1, "[i] [clock rate] -- Manchester demodulate binary stream (option 'i' to invert output)"},\r
2849 {"manmod", Cmdmanchestermod, 1, "[clock rate] -- Manchester modulate a binary stream"},\r
2850 {"norm", CmdNorm, 1, "Normalize max/min to +/-500"},\r
2851 {"plot", CmdPlot, 1, "Show graph window"},\r
2852 {"quit", CmdQuit, 1, "Quit program"},\r
2853 {"readmem", CmdReadmem, 0, "[address] -- Read memory at decimal address from flash"},\r
2854 {"reset", CmdReset, 0, "Reset the Proxmark3"},\r
2855 {"save", CmdSave, 1, "<filename> -- Save trace (from graph window)"},\r
2856 {"scale", CmdScale, 1, "<int> -- Set cursor display scale"},\r
2857 {"setlfdivisor", CmdSetDivisor, 0, "<19 - 255> -- Drive LF antenna at 12Mhz/(divisor+1)"},\r
2858 {"sri512read", CmdSri512read, 0, "<int> -- Read contents of a SRI512 tag"},\r
2859 {"tidemod", CmdTIDemod, 1, "Demodulate raw bits for TI-type LF tag"},\r
2860 {"tiread", CmdTIRead, 0, "Read and decode a TI 134 kHz tag"},\r
2861 {"tiwrite", CmdTIWrite, 0, "Write new data to a r/w TI 134 kHz tag"},\r
2862 {"threshold", CmdThreshold, 1, "Maximize/minimize every value in the graph window depending on threshold"},\r
2863 {"tune", CmdTune, 0, "Measure antenna tuning"},\r
2864 {"version", CmdVersion, 0, "Show version inforation about the connected Proxmark"},\r
2865 {"vchdemod", CmdVchdemod, 0, "['clone'] -- Demodulate samples for VeriChip"},\r
2866 {"zerocrossings", CmdZerocrossings, 1, "Count time between zero-crossings"},\r
2867};\r
2868\r
2869static struct {\r
2870 char *name;\r
2871 char *args;\r
2872 char *argshelp;\r
2873 char *description;\r
2874 } CommandExtendedHelp[]= {\r
2875 {"detectreader","'l'|'h'","'l' specifies LF antenna scan only, 'h' specifies HF antenna scan only.","Monitor antenna for changes in voltage. Output is in three fields: CHANGED, CURRENT, PERIOD,\nwhere CHANGED is the value just changed from, CURRENT is the current value and PERIOD is the\nnumber of program loops since the last change.\n\nThe RED LED indicates LF field detected, and the GREEN LED indicates HF field detected."},\r
2876 {"tune","","","Drive LF antenna at all divisor range values (19 - 255) and store the results in the output\nbuffer. Issuing 'losamples' and then 'plot' commands will display the resulting peak. 12MHz\ndivided by the peak's position plus one gives the antenna's resonant frequency. For convenience,\nthis value is also printed out by the command."},\r
2877 };\r
2878\r
2879//-----------------------------------------------------------------------------\r
2880// Entry point into our code: called whenever the user types a command and\r
2881// then presses Enter, which the full command line that they typed.\r
2882//-----------------------------------------------------------------------------\r
2883void CommandReceived(char *cmd)\r
2884{\r
2885 int i;\r
2886 char line[256];\r
2887\r
2888 PrintToScrollback("> %s", cmd);\r
2889\r
2890 if(strcmp(cmd, "help") == 0 || strncmp(cmd,"help ",strlen("help ")) == 0) {\r
2891 // check if we're doing extended help\r
2892 if(strlen(cmd) > strlen("help ")) {\r
2893 cmd += strlen("help ");\r
2894 for(i = 0; i < sizeof(CommandExtendedHelp) / sizeof(CommandExtendedHelp[0]); i++) {\r
2895 if(strcmp(CommandExtendedHelp[i].name,cmd) == 0) {\r
2896 PrintToScrollback("\nExtended help for '%s':\n", cmd);\r
2897 PrintToScrollback("Args: %s\t- %s\n",CommandExtendedHelp[i].args,CommandExtendedHelp[i].argshelp);\r
2898 PrintToScrollback(CommandExtendedHelp[i].description);\r
2899 PrintToScrollback("");\r
2900 return;\r
2901 }\r
2902 }\r
2903 PrintToScrollback("No extended help available for '%s'", cmd);\r
2904 return;\r
2905 }\r
2906 if (offline) PrintToScrollback("Operating in OFFLINE mode (no device connected)");\r
2907 PrintToScrollback("\r\nAvailable commands:");\r
2908 for(i = 0; i < sizeof(CommandTable) / sizeof(CommandTable[0]); i++) {\r
2909 if (offline && (CommandTable[i].offline==0)) continue;\r
2910 memset(line, ' ', sizeof(line));\r
2911 strcpy(line+2, CommandTable[i].name);\r
2912 line[strlen(line)] = ' ';\r
2913 sprintf(line+15, " -- %s", CommandTable[i].docString);\r
2914 PrintToScrollback("%s", line);\r
2915 }\r
2916 PrintToScrollback("");\r
2917 PrintToScrollback("'help <command>' for extended help on that command\n");\r
2918 return;\r
2919 }\r
2920\r
2921 for(i = 0; i < sizeof(CommandTable) / sizeof(CommandTable[0]); i++) {\r
2922 char *name = CommandTable[i].name;\r
2923 if(memcmp(cmd, name, strlen(name))==0 &&\r
2924 (cmd[strlen(name)] == ' ' || cmd[strlen(name)] == '\0'))\r
2925 {\r
2926 cmd += strlen(name);\r
2927 while(*cmd == ' ') {\r
2928 cmd++;\r
2929 }\r
2930 if (offline && (CommandTable[i].offline==0)) {\r
2931 PrintToScrollback("Offline mode, cannot use this command.");\r
2932 return;\r
2933 }\r
2934 (CommandTable[i].handler)(cmd);\r
2935 return;\r
2936 }\r
2937 }\r
2938 PrintToScrollback(">> bad command '%s'", cmd);\r
2939}\r
2940\r
2941//-----------------------------------------------------------------------------\r
2942// Entry point into our code: called whenever we received a packet over USB\r
2943// that we weren't necessarily expecting, for example a debug print.\r
2944//-----------------------------------------------------------------------------\r
2945void UsbCommandReceived(UsbCommand *c)\r
2946{\r
2947 switch(c->cmd) {\r
2948 case CMD_DEBUG_PRINT_STRING: {\r
2949 char s[100];\r
2950 if(c->ext1 > 70 || c->ext1 < 0) {\r
2951 c->ext1 = 0;\r
2952 }\r
2953 memcpy(s, c->d.asBytes, c->ext1);\r
2954 s[c->ext1] = '\0';\r
2955 PrintToScrollback("#db# %s", s);\r
2956 break;\r
2957 }\r
2958\r
2959 case CMD_DEBUG_PRINT_INTEGERS:\r
2960 PrintToScrollback("#db# %08x, %08x, %08x\r\n", c->ext1, c->ext2, c->ext3);\r
2961 break;\r
2962\r
2963 case CMD_MEASURED_ANTENNA_TUNING: {\r
2964 int peakv, peakf;\r
2965 int vLf125, vLf134, vHf;\r
2966 vLf125 = c->ext1 & 0xffff;\r
2967 vLf134 = c->ext1 >> 16;\r
2968 vHf = c->ext2 & 0xffff;;\r
2969 peakf = c->ext3 & 0xffff;\r
2970 peakv = c->ext3 >> 16;\r
2971 PrintToScrollback("");\r
2972 PrintToScrollback("");\r
2973 PrintToScrollback("# LF antenna: %5.2f V @ 125.00 kHz", vLf125/1000.0);\r
2974 PrintToScrollback("# LF antenna: %5.2f V @ 134.00 kHz", vLf134/1000.0);\r
2975 PrintToScrollback("# LF optimal: %5.2f V @%9.2f kHz", peakv/1000.0, 12000.0/(peakf+1));\r
2976 PrintToScrollback("# HF antenna: %5.2f V @ 13.56 MHz", vHf/1000.0);\r
2977 if (peakv<2000)\r
2978 PrintToScrollback("# Your LF antenna is unusable.");\r
2979 else if (peakv<10000)\r
2980 PrintToScrollback("# Your LF antenna is marginal.");\r
2981 if (vHf<2000)\r
2982 PrintToScrollback("# Your HF antenna is unusable.");\r
2983 else if (vHf<5000)\r
2984 PrintToScrollback("# Your HF antenna is marginal.");\r
2985 break;\r
2986 }\r
2987 default:\r
2988 PrintToScrollback("unrecognized command %08x\n", c->cmd);\r
2989 break;\r
2990 }\r
2991}\r
Impressum, Datenschutz