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