fix little endian vs big endian in the macros... again... but this time correct
[RRG-proxmark3.git] / client / src / cmddata.c
blob4371a01871a0b48df5a6a569af09a3be7e76f2c4
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
3 // modified Marshmellow,
4 // modified Iceman 2019, 2020, 2021
5 //
6 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
7 // at your option, any later version. See the LICENSE.txt file for the text of
8 // the license.
9 //-----------------------------------------------------------------------------
10 // Data and Graph commands
11 //-----------------------------------------------------------------------------
12 #include "cmddata.h"
13 #include <stdio.h>
14 #include <string.h>
15 #include <limits.h> // for CmdNorm INT_MIN && INT_MAX
16 #include <math.h> // pow
17 #include <ctype.h> // tolower
18 #include "commonutil.h" // ARRAYLEN
19 #include "cmdparser.h" // for command_t
20 #include "ui.h" // for show graph controls
21 #include "proxgui.h"
22 #include "graph.h" // for graph data
23 #include "comms.h"
24 #include "lfdemod.h" // for demod code
25 #include "loclass/cipherutils.h" // for decimating samples in getsamples
26 #include "cmdlfem410x.h" // askem410xdecode
27 #include "fileutils.h" // searchFile
28 #include "cliparser.h"
29 #include "cmdlft55xx.h" // print...
30 #include "crypto/asn1utils.h" // ASN1 decode / print
32 uint8_t DemodBuffer[MAX_DEMOD_BUF_LEN];
33 size_t DemodBufferLen = 0;
34 int32_t g_DemodStartIdx = 0;
35 int g_DemodClock = 0;
37 static int CmdHelp(const char *Cmd);
39 //set the demod buffer with given array ofq binary (one bit per byte)
40 //by marshmellow
41 void setDemodBuff(uint8_t *buff, size_t size, size_t start_idx) {
42 if (buff == NULL) return;
44 if (size > MAX_DEMOD_BUF_LEN - start_idx)
45 size = MAX_DEMOD_BUF_LEN - start_idx;
47 for (size_t i = 0; i < size; i++)
48 DemodBuffer[i] = buff[start_idx++];
50 DemodBufferLen = size;
53 bool getDemodBuff(uint8_t *buff, size_t *size) {
54 if (buff == NULL) return false;
55 if (size == NULL) return false;
56 if (*size == 0) return false;
58 *size = (*size > DemodBufferLen) ? DemodBufferLen : *size;
60 memcpy(buff, DemodBuffer, *size);
61 return true;
64 // include <math.h>
65 // Root mean square
67 static double rms(double *v, size_t n) {
68 double sum = 0.0;
69 for (size_t i = 0; i < n; i++)
70 sum += v[i] * v[i];
71 return sqrt(sum / n);
74 static int cmp_int(const void *a, const void *b) {
75 if (*(const int *)a < * (const int *)b)
76 return -1;
77 else
78 return *(const int *)a > *(const int *)b;
80 static int cmp_uint8(const void *a, const void *b) {
81 if (*(const uint8_t *)a < * (const uint8_t *)b)
82 return -1;
83 else
84 return *(const uint8_t *)a > *(const uint8_t *)b;
86 // Median of a array of values
88 static double median_int(int *src, size_t size) {
89 qsort(src, size, sizeof(int), cmp_int);
90 return 0.5 * (src[size / 2] + src[(size - 1) / 2]);
92 static double median_uint8(uint8_t *src, size_t size) {
93 qsort(src, size, sizeof(uint8_t), cmp_uint8);
94 return 0.5 * (src[size / 2] + src[(size - 1) / 2]);
97 // function to compute mean for a series
98 static double compute_mean(const int *data, size_t n) {
99 double mean = 0.0;
100 for (size_t i = 0; i < n; i++)
101 mean += data[i];
102 mean /= n;
103 return mean;
106 // function to compute variance for a series
107 static double compute_variance(const int *data, size_t n) {
108 double variance = 0.0;
109 double mean = compute_mean(data, n);
111 for (size_t i = 0; i < n; i++)
112 variance += pow((data[i] - mean), 2.0);
114 variance /= n;
115 return variance;
118 // Function to compute autocorrelation for a series
119 // Author: Kenneth J. Christensen
120 // - Corrected divide by n to divide (n - lag) from Tobias Mueller
122 static double compute_autoc(const int *data, size_t n, int lag) {
123 double autocv = 0.0; // Autocovariance value
124 double ac_value; // Computed autocorrelation value to be returned
125 double variance; // Computed variance
126 double mean;
128 mean = compute_mean(data, n);
129 variance = compute_variance(data, n);
131 for (size_t i=0; i < (n - lag); i++)
132 autocv += (data[i] - mean) * (data[i+lag] - mean);
134 autocv = (1.0 / (n - lag)) * autocv;
136 // Autocorrelation is autocovariance divided by variance
137 ac_value = autocv / variance;
138 return ac_value;
142 // option '1' to save DemodBuffer any other to restore
143 void save_restoreDB(uint8_t saveOpt) {
144 static uint8_t SavedDB[MAX_DEMOD_BUF_LEN];
145 static size_t SavedDBlen;
146 static bool DB_Saved = false;
147 static size_t savedDemodStartIdx = 0;
148 static int savedDemodClock = 0;
150 if (saveOpt == GRAPH_SAVE) { //save
152 memcpy(SavedDB, DemodBuffer, sizeof(DemodBuffer));
153 SavedDBlen = DemodBufferLen;
154 DB_Saved = true;
155 savedDemodStartIdx = g_DemodStartIdx;
156 savedDemodClock = g_DemodClock;
157 } else if (DB_Saved) { //restore
159 memcpy(DemodBuffer, SavedDB, sizeof(DemodBuffer));
160 DemodBufferLen = SavedDBlen;
161 g_DemodClock = savedDemodClock;
162 g_DemodStartIdx = savedDemodStartIdx;
166 static int CmdSetDebugMode(const char *Cmd) {
167 CLIParserContext *ctx;
168 CLIParserInit(&ctx, "data setdebugmode",
169 "Set debugging level on client side",
170 "data setdebugmode"
172 void *argtable[] = {
173 arg_param_begin,
174 arg_lit0("0", NULL, "no debug messages"),
175 arg_lit0("1", NULL, "debug"),
176 arg_lit0("2", NULL, "verbose debugging"),
177 arg_param_end
179 CLIExecWithReturn(ctx, Cmd, argtable, true);
181 bool dg_0 = arg_get_lit(ctx, 1);
182 bool dg_1 = arg_get_lit(ctx, 2);
183 bool dg_2 = arg_get_lit(ctx, 3);
184 CLIParserFree(ctx);
186 if (dg_0 + dg_1 + dg_2 > 1) {
187 PrintAndLogEx(INFO, "Select only one option");
188 return PM3_EINVARG;
190 if (dg_0)
191 g_debugMode = 0;
193 if (dg_1)
194 g_debugMode = 1;
196 if (dg_2)
197 g_debugMode = 2;
199 switch (g_debugMode) {
200 case 0:
201 PrintAndLogEx(INFO, "client debug level... %u ( no debug messages )", g_debugMode);
202 break;
203 case 1:
204 PrintAndLogEx(INFO, "client debug level... %u ( debug messages )", g_debugMode);
205 break;
206 case 2:
207 PrintAndLogEx(INFO, "client debug level... %u ( verbose debug messages )", g_debugMode);
208 break;
209 default:
210 break;
212 return PM3_SUCCESS;
215 // max output to 512 bits if we have more
216 // doesn't take inconsideration where the demod offset or bitlen found.
217 int printDemodBuff(uint8_t offset, bool strip_leading, bool invert, bool print_hex) {
218 size_t len = DemodBufferLen;
219 if (len == 0) {
220 PrintAndLogEx(WARNING, "Demodbuffer is empty");
221 return PM3_EINVARG;
224 uint8_t *buf = calloc(len, sizeof(uint8_t));
225 if (buf == NULL) {
226 PrintAndLogEx(WARNING, "dail, cannot allocate memory");
227 return PM3_EMALLOC;
229 memcpy(buf, DemodBuffer, len);
231 uint8_t *p = NULL;
233 if (strip_leading) {
234 p = (buf + offset);
236 if (len > (DemodBufferLen - offset))
237 len = (DemodBufferLen - offset);
239 size_t i;
240 for (i = 0; i < len; i++) {
241 if (p[i] == 1) break;
243 offset += i;
246 if (len > (DemodBufferLen - offset)) {
247 len = (DemodBufferLen - offset);
250 if (len > 512) {
251 len = 512;
254 if (invert) {
255 p = (buf + offset);
256 for (size_t i = 0; i < len; i++) {
257 if (p[i] == 1)
258 p[i] = 0;
259 else {
260 if (p[i] == 0)
261 p[i] = 1;
266 if (print_hex) {
267 p = (buf + offset);
268 char hex[512] = {0x00};
269 int num_bits = binarraytohex(hex, sizeof(hex), (char *)p, len);
270 if (num_bits == 0) {
271 p = NULL;
272 free(buf);
273 return PM3_ESOFT;
275 PrintAndLogEx(SUCCESS, "DemodBuffer:\n%s", hex);
276 } else {
277 PrintAndLogEx(SUCCESS, "DemodBuffer:\n%s", sprint_bin_break(buf + offset, len, 32));
280 p = NULL;
281 free(buf);
282 return PM3_SUCCESS;
285 int CmdPrintDemodBuff(const char *Cmd) {
286 CLIParserContext *ctx;
287 CLIParserInit(&ctx, "data print",
288 "Print the data in the DemodBuffer as hex or binary.\n"
289 "Defaults to binary output",
290 "data print"
292 void *argtable[] = {
293 arg_param_begin,
294 arg_lit0("i", "inv", "invert Demodbuffer before printing"),
295 // arg_int0("l","len", "<dec>", "length to print in # of bits or hex characters respectively"),
296 arg_int0("o", "offset", "<dec>", "offset in # of bits"),
297 arg_lit0("s", "strip", "strip leading zeroes, i.e. set offset to first bit equal to one"),
298 arg_lit0("x", "hex", "output in hex (omit for binary output)"),
299 arg_param_end
301 CLIExecWithReturn(ctx, Cmd, argtable, true);
303 bool invert = arg_get_lit(ctx, 1);
304 int os = arg_get_int_def(ctx, 2, 0);
305 bool lstrip = arg_get_lit(ctx, 3);
306 bool print_hex = arg_get_lit(ctx, 4);
307 CLIParserFree(ctx);
309 uint8_t offset = (os & 0xFF);
311 return printDemodBuff(offset, lstrip, invert, print_hex);
314 // this function strictly converts >1 to 1 and <1 to 0 for each sample in the graphbuffer
315 int CmdGetBitStream(const char *Cmd) {
316 CLIParserContext *ctx;
317 CLIParserInit(&ctx, "data getbitstream",
318 "Convert GraphBuffer's value accordingly\n"
319 " - larger or equal to ONE becomes ONE\n"
320 " - less than ONE becomes ZERO",
321 "data getbitstream"
323 void *argtable[] = {
324 arg_param_begin,
325 arg_param_end
327 CLIExecWithReturn(ctx, Cmd, argtable, true);
328 CLIParserFree(ctx);
330 CmdHpf("");
331 for (uint32_t i = 0; i < GraphTraceLen; i++) {
332 GraphBuffer[i] = (GraphBuffer[i] >= 1) ? 1 : 0;
334 RepaintGraphWindow();
335 return PM3_SUCCESS;
338 static int CmdConvertBitStream(const char *Cmd) {
340 CLIParserContext *ctx;
341 CLIParserInit(&ctx, "data convertbitstream",
342 "Convert GraphBuffer's 0|1 values to 127|-127",
343 "data convertbitstream"
345 void *argtable[] = {
346 arg_param_begin,
347 arg_param_end
349 CLIExecWithReturn(ctx, Cmd, argtable, true);
350 CLIParserFree(ctx);
352 if (isGraphBitstream()) {
353 convertGraphFromBitstream();
354 } else {
355 // get high, low
356 convertGraphFromBitstreamEx(-126, -127);
358 return PM3_SUCCESS;
361 // Cmd Args: Clock, invert, maxErr, maxLen as integers and amplify as char == 'a'
362 // (amp may not be needed anymore)
363 // verbose will print results and demoding messages
364 // emSearch will auto search for EM410x format in bitstream
365 // askType switches decode: ask/raw = 0, ask/manchester = 1
366 int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxlen, bool amplify, bool verbose, bool emSearch, uint8_t askType, bool *stCheck) {
367 PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) clk %i invert %i maxErr %i maxLen %zu amplify %i verbose %i emSearch %i askType %i "
368 , clk
369 , invert
370 , maxErr
371 , maxlen
372 , amplify
373 , verbose
374 , emSearch
375 , askType
377 uint8_t askamp = 0;
379 if (maxlen == 0)
380 maxlen = pm3_capabilities.bigbuf_size;
382 uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
383 if (bits == NULL) {
384 PrintAndLogEx(INFO, "failed to allocate memory");
385 return PM3_EMALLOC;
388 size_t bitlen = getFromGraphBuf(bits);
390 PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) #samples from graphbuff: %zu", bitlen);
392 if (bitlen < 255) {
393 free(bits);
394 return PM3_ESOFT;
397 if (maxlen < bitlen && maxlen != 0)
398 bitlen = maxlen;
400 int foundclk = 0;
402 //amplify signal before ST check
403 if (amplify) {
404 askAmp(bits, bitlen);
407 size_t ststart = 0, stend = 0;
408 // if (*stCheck)
409 bool st = DetectST(bits, &bitlen, &foundclk, &ststart, &stend);
411 if (clk == 0) {
412 if (foundclk == 32 || foundclk == 64) {
413 clk = foundclk;
417 if (st) {
418 *stCheck = st;
419 CursorCPos = ststart;
420 CursorDPos = stend;
421 if (verbose)
422 PrintAndLogEx(DEBUG, "Found Sequence Terminator - First one is shown by orange / blue graph markers");
425 int start_idx = 0;
426 int errCnt = askdemod_ext(bits, &bitlen, &clk, &invert, maxErr, askamp, askType, &start_idx);
428 if (errCnt < 0 || bitlen < 16) { //if fatal error (or -1)
429 PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) No data found errors:%d, %s bitlen:%zu, clock:%d"
430 , errCnt
431 , (invert) ? "inverted," : ""
432 , bitlen
433 , clk
435 free(bits);
436 return PM3_ESOFT;
439 if (errCnt > maxErr) {
440 PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%zu, clock:%d"
441 , errCnt
442 , bitlen
443 , clk
445 free(bits);
446 return PM3_ESOFT;
449 if (verbose) {
450 PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) using clock:%d, %sbits found:%zu, start index %d"
451 , clk
452 , (invert) ? "inverted, " : ""
453 , bitlen
454 , start_idx
458 //output
459 setDemodBuff(bits, bitlen, 0);
460 setClockGrid(clk, start_idx);
462 if (verbose) {
463 if (errCnt > 0)
464 PrintAndLogEx(DEBUG, "# Errors during demoding (shown as 7 in bit stream): %d", errCnt);
466 if (askType) {
467 PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Manchester") " - clock %d - decoded bitstream", clk);
468 PrintAndLogEx(INFO, "---------------------------------------------");
469 } else {
470 PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Raw") " - clock %d - decoded bitstream", clk);
471 PrintAndLogEx(INFO, "--------------------------------------");
474 printDemodBuff(0, false, false, false);
476 uint64_t lo = 0;
477 uint32_t hi = 0;
478 if (emSearch)
479 AskEm410xDecode(true, &hi, &lo);
481 free(bits);
482 return PM3_SUCCESS;
485 int ASKDemod(int clk, int invert, int maxErr, size_t maxlen, bool amplify, bool verbose, bool emSearch, uint8_t askType) {
486 bool st = false;
487 return ASKDemod_ext(clk, invert, maxErr, maxlen, amplify, verbose, emSearch, askType, &st);
490 // takes 5 arguments - clock, invert, maxErr, maxLen as integers and amplify as char == 'a'
491 // attempts to demodulate ask while decoding manchester
492 // prints binary found and saves in graphbuffer for further commands
493 static int Cmdaskmandemod(const char *Cmd) {
494 CLIParserContext *ctx;
495 CLIParserInit(&ctx, "data rawdemod --am",
496 "ASK/MANCHESTER demodulate the data in the GraphBuffer and output binary",
497 "data rawdemod --am --> demod a ask/manchester tag, using autodetect\n"
498 "data rawdemod --am -c 32 --> demod a ask/manchester tag, using a clock of RF/32\n"
499 "data rawdemod --am -i --> demod a ask/manchester tag, using autodetect, invert output\n"
500 "data rawdemod --am -c 32 -i --> demod a ask/manchester tag, using a clock of RF/32, invert output\n"
501 "data rawdemod --am -c 64 -i --max 0 --> demod a ask/manchester tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
503 void *argtable[] = {
504 arg_param_begin,
505 arg_lit0("a", "amp", "try attempt demod with ask amplification (def no amp)"),
506 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
507 arg_lit0("i", "inv", "invert output"),
508 arg_lit0("s", "st", "check for sequence terminator"),
509 arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 100)"),
510 arg_int0(NULL, "samples", "<dec>", "maximum samples to read (def 32768) [512 bits at RF/64]"),
511 arg_param_end
513 CLIExecWithReturn(ctx, Cmd, argtable, true);
515 bool amplify = arg_get_lit(ctx, 1);
516 uint8_t clk = (uint8_t)arg_get_int_def(ctx, 2, 0) & 0xFF;
517 bool invert = arg_get_lit(ctx, 3);
518 bool st = arg_get_lit(ctx, 4);
519 uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 5, 100) & 0xFF;
520 size_t max_len = (size_t)arg_get_int_def(ctx, 6, 0) & 0xFF;
521 CLIParserFree(ctx);
523 return ASKDemod_ext(clk, invert, max_err, max_len, amplify, true, true, 1, &st);
526 // manchester decode
527 // strictly take 10 and 01 and convert to 0 and 1
528 static int Cmdmandecoderaw(const char *Cmd) {
529 CLIParserContext *ctx;
530 CLIParserInit(&ctx, "data manrawdecode",
531 "Manchester decode binary stream in DemodBuffer\n"
532 "Converts 10 and 01 and converts to 0 and 1 respectively\n"
533 " - must have binary sequence in demodbuffer (run `data rawdemod --ar` before)",
534 "data manrawdecode"
536 void *argtable[] = {
537 arg_param_begin,
538 arg_lit0("i", "inv", "invert output"),
539 arg_int0(NULL, "err", "<dec>", "set max errors tolerated (def 20)"),
540 arg_param_end
542 CLIExecWithReturn(ctx, Cmd, argtable, true);
543 bool invert = arg_get_lit(ctx, 1);
544 int max_err = arg_get_int_def(ctx, 2, 20);
545 CLIParserFree(ctx);
547 if (DemodBufferLen == 0) {
548 PrintAndLogEx(WARNING, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`"));
549 return PM3_ESOFT;
552 uint8_t bits[MAX_DEMOD_BUF_LEN] = {0};
554 // make sure its just binary data 0|1|7 in buffer
555 int high = 0, low = 0;
556 size_t i = 0;
557 for (; i < DemodBufferLen; ++i) {
558 if (DemodBuffer[i] > high)
559 high = DemodBuffer[i];
560 else if (DemodBuffer[i] < low)
561 low = DemodBuffer[i];
562 bits[i] = DemodBuffer[i];
565 if (high > 7 || low < 0) {
566 PrintAndLogEx(ERR, "Error: please first raw demod then manchester raw decode");
567 return PM3_ESOFT;
570 size_t size = i;
571 uint8_t offset = 0;
572 uint16_t err_cnt = manrawdecode(bits, &size, invert, &offset);
573 if (err_cnt > max_err) {
574 PrintAndLogEx(ERR, "Too many errors attempting to decode " _RED_("%i"), err_cnt);
575 return PM3_ESOFT;
578 if (err_cnt > 0) {
579 PrintAndLogEx(WARNING, "# %i errors found during demod (shown as " _YELLOW_(".")" in bit stream) ", err_cnt);
582 PrintAndLogEx(INFO, "Manchester decoded %s", (invert) ? "( inverted )" : "");
583 PrintAndLogEx(INFO, "%s", sprint_bin_break(bits, size, 32));
585 // try decode EM410x
586 if (err_cnt == 0) {
587 uint64_t id = 0;
588 uint32_t hi = 0;
589 size_t idx = 0;
590 if (Em410xDecode(bits, &size, &idx, &hi, &id) == 1) {
591 //need to adjust to set bitstream back to manchester encoded data
592 //setDemodBuff(bits, size, idx);
593 printEM410x(hi, id, false);
597 setClockGrid(g_DemodClock, g_DemodStartIdx + g_DemodClock / 2);
598 return PM3_SUCCESS;
602 * @author marshmellow
603 * biphase decode
604 * decodes 01 or 10 -> ZERO
605 * 11 or 00 -> ONE
606 * param offset adjust start position
607 * param invert invert output
608 * param masxErr maximum tolerated errors
610 static int CmdBiphaseDecodeRaw(const char *Cmd) {
611 CLIParserContext *ctx;
612 CLIParserInit(&ctx, "data biphaserawdecode",
613 "Biphase decode binary stream in DemodBuffer\n"
614 "Converts 10 or 01 -> 1 and 11 or 00 -> 0\n"
615 " - must have binary sequence in demodbuffer (run `data rawdemod --ar` before)\n"
616 " - invert for Conditional Dephase Encoding (CDP) AKA Differential Manchester",
617 "data biphaserawdecode --> decode biphase bitstream from the demodbuffer\n"
618 "data biphaserawdecode -oi --> decode biphase bitstream from the demodbuffer, adjust offset, and invert output"
620 void *argtable[] = {
621 arg_param_begin,
622 arg_lit0("o", "offset", "set to adjust decode start position"),
623 arg_lit0("i", "inv", "invert output"),
624 arg_int0(NULL, "err", "<dec>", "set max errors tolerated (def 20)"),
625 arg_param_end
627 CLIExecWithReturn(ctx, Cmd, argtable, true);
628 int offset = arg_get_lit(ctx, 1);
629 bool invert = arg_get_lit(ctx, 2);
630 int max_err = arg_get_int_def(ctx, 3, 20);
631 CLIParserFree(ctx);
633 if (DemodBufferLen == 0) {
634 PrintAndLogEx(WARNING, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`"));
635 return PM3_ESOFT;
638 uint8_t bits[MAX_DEMOD_BUF_LEN] = {0};
639 size_t size = sizeof(bits);
640 if (!getDemodBuff(bits, &size)) return PM3_ESOFT;
642 int err_cnt = BiphaseRawDecode(bits, &size, &offset, invert);
643 if (err_cnt < 0) {
644 PrintAndLogEx(ERR, "Error during decode " _RED_("%i"), err_cnt);
645 return PM3_ESOFT;
647 if (err_cnt > max_err) {
648 PrintAndLogEx(ERR, "Too many errors attempting to decode " _RED_("%i"), err_cnt);
649 return PM3_ESOFT;
652 if (err_cnt > 0) {
653 PrintAndLogEx(WARNING, "# %i errors found during demod (shown as " _YELLOW_(".")" in bit stream) ", err_cnt);
656 PrintAndLogEx(INFO, "Biphase decoded using offset %d%s", offset, (invert) ? "( inverted )" : "");
657 PrintAndLogEx(INFO, "%s", sprint_bin_break(bits, size, 32));
659 //remove first bit from raw demod
660 if (offset) {
661 setDemodBuff(DemodBuffer, DemodBufferLen - offset, offset);
664 setClockGrid(g_DemodClock, g_DemodStartIdx + g_DemodClock * offset / 2);
665 return PM3_SUCCESS;
668 // ASK Demod then Biphase decode GraphBuffer samples
669 int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) {
670 //ask raw demod GraphBuffer first
672 uint8_t bs[MAX_DEMOD_BUF_LEN];
673 size_t size = getFromGraphBuf(bs);
674 if (size == 0) {
675 PrintAndLogEx(DEBUG, "DEBUG: no data in graphbuf");
676 return PM3_ESOFT;
678 int startIdx = 0;
679 //invert here inverts the ask raw demoded bits which has no effect on the demod, but we need the pointer
680 int errCnt = askdemod_ext(bs, &size, &clk, &invert, maxErr, 0, 0, &startIdx);
681 if (errCnt < 0 || errCnt > maxErr) {
682 PrintAndLogEx(DEBUG, "DEBUG: no data or error found %d, clock: %d", errCnt, clk);
683 return PM3_ESOFT;
686 //attempt to Biphase decode BitStream
687 errCnt = BiphaseRawDecode(bs, &size, &offset, invert);
688 if (errCnt < 0) {
689 if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: Error BiphaseRawDecode: %d", errCnt);
690 return PM3_ESOFT;
692 if (errCnt > maxErr) {
693 if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: Error BiphaseRawDecode too many errors: %d", errCnt);
694 return PM3_ESOFT;
697 //success set DemodBuffer and return
698 setDemodBuff(bs, size, 0);
699 setClockGrid(clk, startIdx + clk * offset / 2);
700 if (g_debugMode || verbose) {
701 PrintAndLogEx(DEBUG, "Biphase Decoded using offset %d | clock %d | #errors %d | start index %d\ndata\n", offset, clk, errCnt, (startIdx + clk * offset / 2));
702 printDemodBuff(offset, false, false, false);
704 return PM3_SUCCESS;
707 // see ASKbiphaseDemod
708 static int Cmdaskbiphdemod(const char *Cmd) {
709 CLIParserContext *ctx;
710 CLIParserInit(&ctx, "data rawdemod --ab",
711 "ASK/BIPHASE demodulate the data in the GraphBuffer and output binary\n"
712 "NOTE, `--invert` for Conditional Dephase Encoding (CDP) AKA Differential Manchester\n",
713 "data rawdemod --ab --> demod a ask/biphase tag, using autodetect\n"
714 "data rawdemod --ab -c 32 --> demod a ask/biphase tag, using a clock of RF/32\n"
715 "data rawdemod --ab -i --> demod a ask/biphase tag, using autodetect, invert output\n"
716 "data rawdemod --ab -c 32 -i --> demod a ask/biphase tag, using a clock of RF/32, invert output\n"
717 "data rawdemod --ab -c 64 -i --max 0 --> demod a ask/biphase tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
719 void *argtable[] = {
720 arg_param_begin,
721 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
722 arg_lit0("i", "inv", "invert output"),
723 arg_int0("o", "offset", "<dec>", "offset to begin biphase (def 0)"),
724 arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 50)"),
725 arg_param_end
727 CLIExecWithReturn(ctx, Cmd, argtable, true);
729 uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF;
730 bool invert = arg_get_lit(ctx, 2);
731 int offset = arg_get_int_def(ctx, 3, 0);
732 uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 4, 50) & 0xFF;
733 CLIParserFree(ctx);
735 return ASKbiphaseDemod(offset, clk, invert, max_err, true);
738 // see ASKDemod
739 static int Cmdaskrawdemod(const char *Cmd) {
740 CLIParserContext *ctx;
741 CLIParserInit(&ctx, "data rawdemod --ar",
742 "ASK/RAW demodulate the data in the GraphBuffer and output binary",
743 "data rawdemod --ar -a --> demod a ask tag, using autodetect, amplified\n"
744 "data rawdemod --ar -c 32 --> demod a ask tag, using a clock of RF/32\n"
745 "data rawdemod --ar -i --> demod a ask tag, using autodetect, invert output\n"
746 "data rawdemod --ar -c 32 -i --> demod a ask tag, using a clock of RF/32, invert output\n"
747 "data rawdemod --ar -c 64 -i --max 0 --> demod a ask tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
749 void *argtable[] = {
750 arg_param_begin,
751 arg_lit0("a", "amp", "try attempt demod with ask amplification (def no amp)"),
752 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
753 arg_lit0("i", "inv", "invert output"),
754 arg_lit0("s", "st", "check for sequence terminator"),
755 arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 100)"),
756 arg_int0(NULL, "samples", "<dec>", "maximum samples to read (def 32768) [512 bits at RF/64]"),
757 arg_param_end
759 CLIExecWithReturn(ctx, Cmd, argtable, true);
761 bool amplify = arg_get_lit(ctx, 1);
762 uint8_t clk = (uint8_t)arg_get_int_def(ctx, 2, 0) & 0xFF;
763 bool invert = arg_get_lit(ctx, 3);
764 bool st = arg_get_lit(ctx, 4);
765 uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 5, 100) & 0xFF;
766 size_t max_len = (size_t)arg_get_int_def(ctx, 6, 0) & 0xFF;
767 CLIParserFree(ctx);
769 return ASKDemod_ext(clk, invert, max_err, max_len, amplify, true, false, 0, &st);
772 int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveGrph, bool verbose) {
773 // sanity check
774 if (window > len) window = len;
776 if (verbose) PrintAndLogEx(INFO, "performing " _YELLOW_("%zu") " correlations", GraphTraceLen - window);
778 //test
779 double autocv = 0.0; // Autocovariance value
780 size_t correlation = 0;
781 int lastmax = 0;
783 // in, len, 4000
784 double mean = compute_mean(in, len);
785 // Computed variance
786 double variance = compute_variance(in, len);
788 int *correl_buf = calloc(MAX_GRAPH_TRACE_LEN, sizeof(int));
790 for (size_t i = 0; i < len - window; ++i) {
792 for (size_t j = 0; j < (len - i); j++) {
793 autocv += (in[j] - mean) * (in[j + i] - mean);
795 autocv = (1.0 / (len - i)) * autocv;
797 correl_buf[i] = autocv;
799 // Computed autocorrelation value to be returned
800 // Autocorrelation is autocovariance divided by variance
801 double ac_value = autocv / variance;
803 // keep track of which distance is repeating.
804 if (ac_value > 1) {
805 correlation = i - lastmax;
806 lastmax = i;
811 int hi = 0, idx = 0;
812 int distance = 0, hi_1 = 0, idx_1 = 0;
813 for (size_t i = 0; i <= len; ++i) {
814 if (correl_buf[i] > hi) {
815 hi = correl_buf[i];
816 idx = i;
820 for (size_t i = idx + 1; i <= window; ++i) {
821 if (correl_buf[i] > hi_1) {
822 hi_1 = correl_buf[i];
823 idx_1 = i;
827 int foo = ABS(hi - hi_1);
828 int bar = (int)((int)((hi + hi_1) / 2) * 0.04);
830 if (verbose && foo < bar) {
831 distance = idx_1 - idx;
832 PrintAndLogEx(SUCCESS, "possible visible correlation "_YELLOW_("%4d") " samples", distance);
833 } else if (verbose && (correlation > 1)) {
834 PrintAndLogEx(SUCCESS, "possible correlation " _YELLOW_("%4zu") " samples", correlation);
835 } else {
836 PrintAndLogEx(FAILED, "no repeating pattern found, try increasing window size");
839 int retval = correlation;
840 if (SaveGrph) {
841 //GraphTraceLen = GraphTraceLen - window;
842 memcpy(out, correl_buf, len * sizeof(int));
843 if (distance > 0) {
844 setClockGrid(distance, idx);
845 retval = distance;
846 } else
847 setClockGrid(correlation, idx);
849 CursorCPos = idx_1;
850 CursorDPos = idx_1 + retval;
851 DemodBufferLen = 0;
852 RepaintGraphWindow();
854 free(correl_buf);
855 return retval;
858 static int CmdAutoCorr(const char *Cmd) {
859 CLIParserContext *ctx;
860 CLIParserInit(&ctx, "data autocorr",
861 "Autocorrelate over window is used to detect repeating sequences.\n"
862 "We use it as detection of how long in bits a message inside the signal is",
863 "data autocorr -w 4000\n"
864 "data autocorr -w 4000 -g"
866 void *argtable[] = {
867 arg_param_begin,
868 arg_lit0("g", NULL, "save back to GraphBuffer (overwrite)"),
869 arg_u64_0("w", "win", "<dec>", "window length for correlation. def 4000"),
870 arg_param_end
872 CLIExecWithReturn(ctx, Cmd, argtable, true);
873 bool updateGrph = arg_get_lit(ctx, 1);
874 uint32_t window = arg_get_u32_def(ctx, 2, 4000);
875 CLIParserFree(ctx);
877 PrintAndLogEx(INFO, "Using window size " _YELLOW_("%u"), window);
879 if (GraphTraceLen == 0) {
880 PrintAndLogEx(WARNING, "GraphBuffer is empty");
881 PrintAndLogEx(HINT, "Try `" _YELLOW_("lf read") "` to collect samples");
882 return PM3_ESOFT;
885 if (window >= GraphTraceLen) {
886 PrintAndLogEx(WARNING, "window must be smaller than trace (" _YELLOW_("%zu") " samples)", GraphTraceLen);
887 return PM3_EINVARG;
890 AutoCorrelate(GraphBuffer, GraphBuffer, GraphTraceLen, window, updateGrph, true);
891 return PM3_SUCCESS;
894 static int CmdBitsamples(const char *Cmd) {
896 CLIParserContext *ctx;
897 CLIParserInit(&ctx, "data bitsamples",
898 "Get raw samples from device as bitstring",
899 "data bitsamples"
901 void *argtable[] = {
902 arg_param_begin,
903 arg_param_end
905 CLIExecWithReturn(ctx, Cmd, argtable, true);
906 CLIParserFree(ctx);
908 int cnt = 0;
909 uint8_t got[12288];
911 if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) {
912 PrintAndLogEx(WARNING, "command execution time out");
913 return PM3_ETIMEOUT;
916 for (size_t j = 0; j < ARRAYLEN(got); j++) {
917 for (uint8_t k = 0; k < 8; k++) {
918 if (got[j] & (1 << (7 - k)))
919 GraphBuffer[cnt++] = 1;
920 else
921 GraphBuffer[cnt++] = 0;
924 GraphTraceLen = cnt;
925 RepaintGraphWindow();
926 return PM3_SUCCESS;
929 static int CmdBuffClear(const char *Cmd) {
930 CLIParserContext *ctx;
931 CLIParserInit(&ctx, "data clear",
932 "This function clears the bigbuff on deviceside\n"
933 "and graph window",
934 "data clear"
936 void *argtable[] = {
937 arg_param_begin,
938 arg_param_end
940 CLIExecWithReturn(ctx, Cmd, argtable, true);
941 CLIParserFree(ctx);
942 clearCommandBuffer();
943 SendCommandNG(CMD_BUFF_CLEAR, NULL, 0);
944 ClearGraph(true);
945 return PM3_SUCCESS;
948 static int CmdDecimate(const char *Cmd) {
950 CLIParserContext *ctx;
951 CLIParserInit(&ctx, "data decimate",
952 "Performs decimation, by reducing samples N times in the grapbuf. Good for PSK\n",
953 "data decimate\n"
954 "data decimate -n 4"
957 void *argtable[] = {
958 arg_param_begin,
959 arg_int0("n", NULL, "<dec>", "factor to reduce sample set (default 2)"),
960 arg_param_end
962 CLIExecWithReturn(ctx, Cmd, argtable, true);
963 int n = arg_get_int_def(ctx, 1, 2);
964 CLIParserFree(ctx);
966 for (size_t i = 0; i < (GraphTraceLen / n); ++i)
967 GraphBuffer[i] = GraphBuffer[i * n];
969 GraphTraceLen /= n;
970 PrintAndLogEx(SUCCESS, "decimated by " _GREEN_("%u"), n);
971 RepaintGraphWindow();
972 return PM3_SUCCESS;
975 * Undecimate - I'd call it 'interpolate', but we'll save that
976 * name until someone does an actual interpolation command, not just
977 * blindly repeating samples
978 * @param Cmd
979 * @return
981 static int CmdUndecimate(const char *Cmd) {
982 CLIParserContext *ctx;
983 CLIParserInit(&ctx, "data undecimate",
984 "Performs un-decimation, by repeating each sample N times in the graphbuf",
985 "data undecimate\n"
986 "data undecimate -n 4\n"
989 void *argtable[] = {
990 arg_param_begin,
991 arg_int0("n", NULL, "<dec>", "factor to repeat each sample (default 2)"),
992 arg_param_end
994 CLIExecWithReturn(ctx, Cmd, argtable, true);
995 int factor = arg_get_int_def(ctx, 1, 2);
996 CLIParserFree(ctx);
998 //We have memory, don't we?
999 int swap[MAX_GRAPH_TRACE_LEN] = {0};
1000 uint32_t g_index = 0, s_index = 0;
1001 while (g_index < GraphTraceLen && s_index + factor < MAX_GRAPH_TRACE_LEN) {
1002 int count = 0;
1003 for (count = 0; count < factor && s_index + count < MAX_GRAPH_TRACE_LEN; count++) {
1004 swap[s_index + count] = (
1005 (double)(factor - count) / (factor - 1)) * GraphBuffer[g_index] +
1006 ((double)count / factor) * GraphBuffer[g_index + 1]
1009 s_index += count;
1010 g_index++;
1013 memcpy(GraphBuffer, swap, s_index * sizeof(int));
1014 GraphTraceLen = s_index;
1015 RepaintGraphWindow();
1016 return PM3_SUCCESS;
1019 // shift graph zero up or down based on input + or -
1020 static int CmdGraphShiftZero(const char *Cmd) {
1022 CLIParserContext *ctx;
1023 CLIParserInit(&ctx, "data shiftgraphzero",
1024 "Shift 0 for Graphed wave + or - shift value",
1025 "data shiftgraphzero -n 10 --> shift 10 points\n"
1026 "data shiftgraphzero -n -22 --> shift negative 22 points"
1028 void *argtable[] = {
1029 arg_param_begin,
1030 arg_int1("n", NULL, "<dec>", "shift + or -"),
1031 arg_param_end
1033 CLIExecWithReturn(ctx, Cmd, argtable, false);
1034 int shift = arg_get_int_def(ctx, 1, 0);
1035 CLIParserFree(ctx);
1037 for (size_t i = 0; i < GraphTraceLen; i++) {
1038 int shiftedVal = GraphBuffer[i] + shift;
1040 if (shiftedVal > 127)
1041 shiftedVal = 127;
1042 else if (shiftedVal < -127)
1043 shiftedVal = -127;
1044 GraphBuffer[i] = shiftedVal;
1046 CmdNorm("");
1047 return PM3_SUCCESS;
1050 int AskEdgeDetect(const int *in, int *out, int len, int threshold) {
1051 int last = 0;
1052 for (int i = 1; i < len; i++) {
1053 if (in[i] - in[i - 1] >= threshold) //large jump up
1054 last = 127;
1055 else if (in[i] - in[i - 1] <= -1 * threshold) //large jump down
1056 last = -127;
1057 out[i - 1] = last;
1059 return PM3_SUCCESS;
1062 // use large jumps in read samples to identify edges of waves and then amplify that wave to max
1063 // similar to dirtheshold, threshold commands
1064 // takes a threshold length which is the measured length between two samples then determines an edge
1065 static int CmdAskEdgeDetect(const char *Cmd) {
1067 CLIParserContext *ctx;
1068 CLIParserInit(&ctx, "data askedgedetect",
1069 "Adjust Graph for manual ASK demod using the length of sample differences\n"
1070 "to detect the edge of a wave",
1071 "data askedgedetect -t 20"
1073 void *argtable[] = {
1074 arg_param_begin,
1075 arg_int0("t", "thres", "<dec>", "threshold, use 20 - 45 (def 25)"),
1076 arg_param_end
1078 CLIExecWithReturn(ctx, Cmd, argtable, true);
1079 int threshold = arg_get_int_def(ctx, 1, 25);
1080 CLIParserFree(ctx);
1082 PrintAndLogEx(INFO, "using threshold " _YELLOW_("%i"), threshold);
1083 int res = AskEdgeDetect(GraphBuffer, GraphBuffer, GraphTraceLen, threshold);
1084 RepaintGraphWindow();
1085 return res;
1088 // Print our clock rate
1089 // uses data from graphbuffer
1090 // adjusted to take char parameter for type of modulation to find the clock - by marshmellow.
1091 static int CmdDetectClockRate(const char *Cmd) {
1092 CLIParserContext *ctx;
1093 CLIParserInit(&ctx, "data detectclock",
1094 "Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer",
1095 "data detectclock -A --> detect clock of an ask wave in GraphBuffer\n"
1096 "data detectclock -F --> detect clock of an fsk wave in GraphBuffer\n"
1097 "data detectclock -N --> detect clock of an psk wave in GraphBuffer\n"
1098 "data detectclock -P --> detect clock of an nrz/direct wave in GraphBuffer"
1100 void *argtable[] = {
1101 arg_param_begin,
1102 arg_lit0("A", "ASK", "specify ASK modulation clock detection"),
1103 arg_lit0("F", "FSK", "specify FSK modulation clock detection"),
1104 arg_lit0("N", "NZR", "specify NZR/DIRECT modulation clock detection"),
1105 arg_lit0("P", "PSK", "specify PSK modulation clock detection"),
1106 arg_param_end
1108 CLIExecWithReturn(ctx, Cmd, argtable, false);
1109 bool a = arg_get_lit(ctx, 1);
1110 bool f = arg_get_lit(ctx, 2);
1111 bool n = arg_get_lit(ctx, 3);
1112 bool p = arg_get_lit(ctx, 4);
1113 CLIParserFree(ctx);
1115 int tmp = (a + f + n + p);
1116 if (tmp > 1) {
1117 PrintAndLogEx(WARNING, "Only specify one modulation");
1118 return PM3_EINVARG;
1121 if (a)
1122 GetAskClock("", true);
1124 if (f)
1125 GetFskClock("", true);
1127 if (n)
1128 GetNrzClock("", true);
1130 if (p)
1131 GetPskClock("", true);
1133 RepaintGraphWindow();
1134 return PM3_SUCCESS;
1137 static char *GetFSKType(uint8_t fchigh, uint8_t fclow, uint8_t invert) {
1138 static char fType[8];
1139 memset(fType, 0x00, 8);
1140 char *fskType = fType;
1142 if (fchigh == 10 && fclow == 8) {
1144 if (invert)
1145 memcpy(fskType, "FSK2a", 5);
1146 else
1147 memcpy(fskType, "FSK2", 4);
1149 } else if (fchigh == 8 && fclow == 5) {
1151 if (invert)
1152 memcpy(fskType, "FSK1", 4);
1153 else
1154 memcpy(fskType, "FSK1a", 5);
1156 } else {
1157 memcpy(fskType, "FSK??", 5);
1159 return fskType;
1162 // fsk raw demod and print binary
1163 // takes 4 arguments - Clock, invert, fchigh, fclow
1164 // defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a))
1165 int FSKrawDemod(uint8_t rfLen, uint8_t invert, uint8_t fchigh, uint8_t fclow, bool verbose) {
1166 //raw fsk demod no manchester decoding no start bit finding just get binary from wave
1167 if (getSignalProperties()->isnoise) {
1168 if (verbose) {
1169 PrintAndLogEx(INFO, "signal looks like noise");
1171 return PM3_ESOFT;
1174 uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
1175 if (bits == NULL) {
1176 PrintAndLogEx(FAILED, "failed to allocate memory");
1177 return PM3_EMALLOC;
1180 size_t bitlen = getFromGraphBuf(bits);
1181 if (bitlen == 0) {
1182 PrintAndLogEx(DEBUG, "DEBUG: no data in graphbuf");
1183 free(bits);
1184 return PM3_ESOFT;
1187 //get field clock lengths
1188 if (!fchigh || !fclow) {
1189 uint16_t fcs = countFC(bits, bitlen, true);
1190 if (!fcs) {
1191 fchigh = 10;
1192 fclow = 8;
1193 } else {
1194 fchigh = (fcs >> 8) & 0x00FF;
1195 fclow = fcs & 0x00FF;
1198 //get bit clock length
1199 if (!rfLen) {
1200 int firstClockEdge = 0; //todo - align grid on graph with this...
1201 rfLen = detectFSKClk(bits, bitlen, fchigh, fclow, &firstClockEdge);
1202 if (!rfLen) rfLen = 50;
1205 int start_idx = 0;
1206 int size = fskdemod(bits, bitlen, rfLen, invert, fchigh, fclow, &start_idx);
1207 if (size > 0) {
1208 setDemodBuff(bits, size, 0);
1209 setClockGrid(rfLen, start_idx);
1211 // Now output the bitstream to the scrollback by line of 16 bits
1212 if (verbose || g_debugMode) {
1213 PrintAndLogEx(DEBUG, "DEBUG: (FSKrawDemod) using clock:%u, %sfc high:%u, fc low:%u"
1214 , rfLen
1215 , (invert) ? "inverted, " : ""
1216 , fchigh
1217 , fclow
1219 PrintAndLogEx(NORMAL, "");
1220 PrintAndLogEx(SUCCESS, _YELLOW_("%s") " decoded bitstream", GetFSKType(fchigh, fclow, invert));
1221 PrintAndLogEx(INFO, "-----------------------");
1222 printDemodBuff(0, false, invert, false);
1224 goto out;
1225 } else {
1226 PrintAndLogEx(DEBUG, "no FSK data found");
1229 out:
1230 free(bits);
1231 return PM3_SUCCESS;
1234 // fsk raw demod and print binary
1235 // takes 4 arguments - Clock, invert, fchigh, fclow
1236 // defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a))
1237 static int CmdFSKrawdemod(const char *Cmd) {
1238 CLIParserContext *ctx;
1239 CLIParserInit(&ctx, "data rawdemod --fs",
1240 "FSK demodulate the data in the GraphBuffer and output binary",
1241 "data rawdemod --fs --> demod an fsk tag, using autodetect\n"
1242 "data rawdemod --fs -c 32 --> demod an fsk tag, using a clock of RF/32, autodetect fc\n"
1243 "data rawdemod --fs -i --> demod an fsk tag, using autodetect, invert output\n"
1244 "data rawdemod --fs -c 32 -i --> demod an fsk tag, using a clock of RF/32, invert output, autodetect fc\n"
1245 "data rawdemod --fs -c 64 --hi 8 --lo 5 --> demod an fsk1 RF/64 tag\n"
1246 "data rawdemod --fs -c 50 --hi 10 --lo 8 --> demod an fsk2 RF/50 tag\n"
1247 "data rawdemod --fs -c 50 -i --hi 10 --lo 8 --> demod an fsk2a RF/50 tag\n"
1249 void *argtable[] = {
1250 arg_param_begin,
1251 arg_int0("c", "clk", "<dec>", "set clock manually (def: autodetect)"),
1252 arg_lit0("i", "inv", "invert output"),
1253 arg_int0(NULL, "hi", "<dec>", "larger field clock length (def: autodetect)"),
1254 arg_int0(NULL, "lo", "<dec>", "small field clock length (def: autodetect)"),
1255 arg_param_end
1257 CLIExecWithReturn(ctx, Cmd, argtable, true);
1259 uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF;
1260 bool invert = arg_get_lit(ctx, 2);
1261 uint8_t fchigh = (uint8_t)arg_get_int_def(ctx, 3, 0) & 0xFF;
1262 uint8_t fclow = (uint8_t)arg_get_int_def(ctx, 4, 0) & 0xFF;
1263 CLIParserFree(ctx);
1265 return FSKrawDemod(clk, invert, fchigh, fclow, true);
1268 // attempt to psk1 demod graph buffer
1269 int PSKDemod(int clk, int invert, int maxErr, bool verbose) {
1270 if (getSignalProperties()->isnoise) {
1271 if (verbose) {
1272 PrintAndLogEx(INFO, "signal looks like noise");
1274 return PM3_ESOFT;
1277 uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
1278 if (bits == NULL) {
1279 PrintAndLogEx(FAILED, "failed to allocate memory");
1280 return PM3_EMALLOC;
1282 size_t bitlen = getFromGraphBuf(bits);
1283 if (bitlen == 0) {
1284 free(bits);
1285 return PM3_ESOFT;
1288 int startIdx = 0;
1289 int errCnt = pskRawDemod_ext(bits, &bitlen, &clk, &invert, &startIdx);
1290 if (errCnt > maxErr) {
1291 if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) Too many errors found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt);
1292 free(bits);
1293 return PM3_ESOFT;
1295 if (errCnt < 0 || bitlen < 16) { //throw away static - allow 1 and -1 (in case of threshold command first)
1296 if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) no data found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt);
1297 free(bits);
1298 return PM3_ESOFT;
1300 if (verbose || g_debugMode) {
1301 PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) Using Clock:%d, invert:%d, Bits Found:%zu", clk, invert, bitlen);
1302 if (errCnt > 0) {
1303 PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) errors during Demoding (shown as 7 in bit stream): %d", errCnt);
1306 //prime demod buffer for output
1307 setDemodBuff(bits, bitlen, 0);
1308 setClockGrid(clk, startIdx);
1309 free(bits);
1310 return PM3_SUCCESS;
1313 // takes 3 arguments - clock, invert, maxErr as integers
1314 // attempts to demodulate nrz only
1315 // prints binary found and saves in demodbuffer for further commands
1316 int NRZrawDemod(int clk, int invert, int maxErr, bool verbose) {
1318 int errCnt = 0, clkStartIdx = 0;
1320 if (getSignalProperties()->isnoise) {
1321 if (verbose) {
1322 PrintAndLogEx(INFO, "signal looks like noise");
1324 return PM3_ESOFT;
1327 uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
1328 if (bits == NULL) {
1329 PrintAndLogEx(FAILED, "failed to allocate memory");
1330 return PM3_EMALLOC;
1333 size_t bitlen = getFromGraphBuf(bits);
1335 if (bitlen == 0) {
1336 free(bits);
1337 return PM3_ESOFT;
1340 errCnt = nrzRawDemod(bits, &bitlen, &clk, &invert, &clkStartIdx);
1341 if (errCnt > maxErr) {
1342 PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Too many errors found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt);
1343 free(bits);
1344 return PM3_ESOFT;
1346 if (errCnt < 0 || bitlen < 16) { //throw away static - allow 1 and -1 (in case of threshold command first)
1347 PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) no data found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt);
1348 free(bits);
1349 return PM3_ESOFT;
1352 if (verbose || g_debugMode) PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Tried NRZ Demod using Clock: %d - invert: %d - Bits Found: %zu", clk, invert, bitlen);
1353 //prime demod buffer for output
1354 setDemodBuff(bits, bitlen, 0);
1355 setClockGrid(clk, clkStartIdx);
1358 if (errCnt > 0 && (verbose || g_debugMode)) PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Errors during Demoding (shown as 7 in bit stream): %d", errCnt);
1359 if (verbose || g_debugMode) {
1360 PrintAndLogEx(NORMAL, "NRZ demoded bitstream:");
1361 // Now output the bitstream to the scrollback by line of 16 bits
1362 printDemodBuff(0, false, invert, false);
1365 free(bits);
1366 return PM3_SUCCESS;
1369 static int CmdNRZrawDemod(const char *Cmd) {
1370 CLIParserContext *ctx;
1371 CLIParserInit(&ctx, "data rawdemod --nr",
1372 "NRZ/DIRECT demodulate the data in the GraphBuffer and output binary",
1373 "data rawdemod --nr --> demod a nrz/direct tag, using autodetect\n"
1374 "data rawdemod --nr -c 32 --> demod a nrz/direct tag, using a clock of RF/32\n"
1375 "data rawdemod --nr -i --> demod a nrz/direct tag, using autodetect, invert output\n"
1376 "data rawdemod --nr -c 32 -i --> demod a nrz/direct tag, using a clock of RF/32, invert output\n"
1377 "data rawdemod --nr -c 64 -i --max 0 --> demod a nrz/direct tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
1379 void *argtable[] = {
1380 arg_param_begin,
1381 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
1382 arg_lit0("i", "inv", "invert output"),
1383 arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 100)"),
1384 arg_param_end
1386 CLIExecWithReturn(ctx, Cmd, argtable, true);
1388 uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF;
1389 bool invert = arg_get_lit(ctx, 2);
1390 uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 3, 100) & 0xFF;
1391 CLIParserFree(ctx);
1393 return NRZrawDemod(clk, invert, max_err, true);
1396 // takes 3 arguments - clock, invert, max_err as integers
1397 // attempts to demodulate psk only
1398 // prints binary found and saves in demodbuffer for further commands
1399 int CmdPSK1rawDemod(const char *Cmd) {
1400 CLIParserContext *ctx;
1401 CLIParserInit(&ctx, "data rawdemod --p1",
1402 "PSK1 demodulate the data in the GraphBuffer and output binary",
1403 "data rawdemod --p1 --> demod a psk1 tag, using autodetect\n"
1404 "data rawdemod --p1 -c 32 --> demod a psk1 tag, using a clock of RF/32\n"
1405 "data rawdemod --p1 -i --> demod a psk1 tag, using autodetect, invert output\n"
1406 "data rawdemod --p1 -c 32 -i --> demod a psk1 tag, using a clock of RF/32, invert output\n"
1407 "data rawdemod --p1 -c 64 -i --max 0 --> demod a psk1 tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
1409 void *argtable[] = {
1410 arg_param_begin,
1411 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
1412 arg_lit0("i", "inv", "invert output"),
1413 arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 100)"),
1414 arg_param_end
1416 CLIExecWithReturn(ctx, Cmd, argtable, true);
1418 uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF;
1419 bool invert = arg_get_lit(ctx, 2);
1420 uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 3, 100) & 0xFF;
1421 CLIParserFree(ctx);
1423 int ans = PSKDemod(clk, invert, max_err, true);
1424 //output
1425 if (ans != PM3_SUCCESS) {
1426 if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans);
1427 return PM3_ESOFT;
1429 PrintAndLogEx(SUCCESS, _YELLOW_("PSK1") " demoded bitstream");
1430 PrintAndLogEx(INFO, "----------------------");
1431 // Now output the bitstream to the scrollback by line of 16 bits
1432 printDemodBuff(0, false, invert, false);
1433 return PM3_SUCCESS;
1436 // takes same args as cmdpsk1rawdemod
1437 static int CmdPSK2rawDemod(const char *Cmd) {
1438 CLIParserContext *ctx;
1439 CLIParserInit(&ctx, "data rawdemod --p2",
1440 "PSK2 demodulate the data in the GraphBuffer and output binary",
1441 "data rawdemod --p2 --> demod a psk2 tag, using autodetect\n"
1442 "data rawdemod --p2 -c 32 --> demod a psk2 tag, using a clock of RF/32\n"
1443 "data rawdemod --p2 -i --> demod a psk2 tag, using autodetect, invert output\n"
1444 "data rawdemod --p2 -c 32 -i --> demod a psk2 tag, using a clock of RF/32, invert output\n"
1445 "data rawdemod --p2 -c 64 -i --max 0 --> demod a psk2 tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
1447 void *argtable[] = {
1448 arg_param_begin,
1449 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
1450 arg_lit0("i", "inv", "invert output"),
1451 arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 100)"),
1452 arg_param_end
1454 CLIExecWithReturn(ctx, Cmd, argtable, true);
1456 uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF;
1457 bool invert = arg_get_lit(ctx, 2);
1458 uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 3, 100) & 0xFF;
1459 CLIParserFree(ctx);
1461 int ans = PSKDemod(clk, invert, max_err, true);
1462 if (ans != PM3_SUCCESS) {
1463 if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans);
1464 return PM3_ESOFT;
1466 psk1TOpsk2(DemodBuffer, DemodBufferLen);
1467 PrintAndLogEx(SUCCESS, _YELLOW_("PSK2") " demoded bitstream");
1468 PrintAndLogEx(INFO, "----------------------");
1469 // Now output the bitstream to the scrollback by line of 16 bits
1470 printDemodBuff(0, false, invert, false);
1471 return PM3_SUCCESS;
1474 // combines all raw demod functions into one menu command
1475 static int CmdRawDemod(const char *Cmd) {
1477 CLIParserContext *ctx;
1478 CLIParserInit(&ctx, "data rawdemod",
1479 "Demodulate the data in the GraphBuffer and output binary",
1480 "data rawdemod --fs --> demod FSK - autodetect\n"
1481 "data rawdemod --ab --> demod ASK/BIPHASE - autodetect\n"
1482 "data rawdemod --am --> demod ASK/MANCHESTER - autodetect\n"
1483 "data rawdemod --ar --> demod ASK/RAW - autodetect\n"
1484 "data rawdemod --nr --> demod NRZ/DIRECT - autodetect\n"
1485 "data rawdemod --p1 --> demod PSK1 - autodetect\n"
1486 "data rawdemod --p2 --> demod PSK2 - autodetect\n"
1488 void *argtable[] = {
1489 arg_param_begin,
1490 arg_lit0(NULL, "ab", "ASK/Biphase demodulation"),
1491 arg_lit0(NULL, "am", "ASK/Manchester demodulation"),
1492 arg_lit0(NULL, "ar", "ASK/Raw demodulation"),
1493 arg_lit0(NULL, "fs", "FSK demodulation"),
1494 arg_lit0(NULL, "nr", "NRZ/Direct demodulation"),
1495 arg_lit0(NULL, "p1", "PSK 1 demodulation"),
1496 arg_lit0(NULL, "p2", "PSK 2 demodulation"),
1497 arg_strn(NULL, NULL, "<params>", 0, 35, "params for sub command"),
1498 arg_param_end
1502 size_t n = MIN(strlen(Cmd), 4);
1503 char tmp[7];
1504 memset(tmp, 0, sizeof(tmp));
1505 strncpy(tmp, Cmd, n);
1507 CLIExecWithReturn(ctx, tmp, argtable, false);
1508 bool ab = arg_get_lit(ctx, 1);
1509 bool am = arg_get_lit(ctx, 2);
1510 bool ar = arg_get_lit(ctx, 3);
1511 bool fs = arg_get_lit(ctx, 4);
1512 bool nr = arg_get_lit(ctx, 5);
1513 bool p1 = arg_get_lit(ctx, 6);
1514 bool p2 = arg_get_lit(ctx, 7);
1515 CLIParserFree(ctx);
1517 int foo = (ab + am + ar + fs + nr + p1 + p2);
1518 if (foo > 1) {
1519 PrintAndLogEx(WARNING, "please, select only one modulation");
1520 return PM3_EINVARG;
1522 if (foo == 0) {
1523 PrintAndLogEx(WARNING, "please, select a modulation");
1524 return PM3_EINVARG;
1527 int ans = 0;
1528 const char *s = Cmd + n;
1529 if (fs)
1530 ans = CmdFSKrawdemod(s);
1531 else if (ab)
1532 ans = Cmdaskbiphdemod(s);
1533 else if (am)
1534 ans = Cmdaskmandemod(s);
1535 else if (ar)
1536 ans = Cmdaskrawdemod(s);
1537 else if (nr)
1538 ans = CmdNRZrawDemod(s);
1539 else if (p1)
1540 ans = CmdPSK1rawDemod(s);
1541 else if (p2)
1542 ans = CmdPSK2rawDemod(s);
1544 return ans;
1547 void setClockGrid(uint32_t clk, int offset) {
1548 g_DemodStartIdx = offset;
1549 g_DemodClock = clk;
1550 if (clk == 0 && offset == 0)
1551 PrintAndLogEx(DEBUG, "DEBUG: (setClockGrid) clear settings");
1552 else
1553 PrintAndLogEx(DEBUG, "DEBUG: (setClockGrid) demodoffset %d, clk %d", offset, clk);
1555 if (offset > clk) offset %= clk;
1556 if (offset < 0) offset += clk;
1558 if (offset > GraphTraceLen || offset < 0) return;
1559 if (clk < 8 || clk > GraphTraceLen) {
1560 GridLocked = false;
1561 GridOffset = 0;
1562 PlotGridX = 0;
1563 PlotGridXdefault = 0;
1564 RepaintGraphWindow();
1565 } else {
1566 GridLocked = true;
1567 GridOffset = offset;
1568 PlotGridX = clk;
1569 PlotGridXdefault = clk;
1570 RepaintGraphWindow();
1574 int CmdGrid(const char *Cmd) {
1575 CLIParserContext *ctx;
1576 CLIParserInit(&ctx, "data grid",
1577 "This function overlay grid on graph plot window.\n"
1578 "use zero value to turn off either",
1579 "data grid --> turn off\n"
1580 "data grid -x 64 -y 50"
1582 void *argtable[] = {
1583 arg_param_begin,
1584 arg_dbl0("x", NULL, "<dec>", "plot grid X coord"),
1585 arg_dbl0("y", NULL, "<dec>", "plot grid Y coord"),
1586 arg_param_end
1588 CLIExecWithReturn(ctx, Cmd, argtable, true);
1589 PlotGridX = arg_get_dbl_def(ctx, 1, 0);
1590 PlotGridY = arg_get_dbl_def(ctx, 2, 0);
1591 CLIParserFree(ctx);
1592 PrintAndLogEx(INFO, "Setting X %.0f Y %.0f", PlotGridX, PlotGridY);
1593 PlotGridXdefault = PlotGridX;
1594 PlotGridYdefault = PlotGridY;
1595 RepaintGraphWindow();
1596 return PM3_SUCCESS;
1599 static int CmdSetGraphMarkers(const char *Cmd) {
1600 CLIParserContext *ctx;
1601 CLIParserInit(&ctx, "data setgraphmarkers",
1602 "Set blue and orange marker in graph window",
1603 "data setgraphmarkers --> turn off\n"
1604 "data setgraphmarkers -a 64 -b 50"
1606 void *argtable[] = {
1607 arg_param_begin,
1608 arg_u64_0("a", NULL, "<dec>", "orange marker"),
1609 arg_u64_0("b", NULL, "<dec>", "blue marker"),
1610 arg_param_end
1612 CLIExecWithReturn(ctx, Cmd, argtable, true);
1613 CursorCPos = arg_get_u32_def(ctx, 1, 0);
1614 CursorDPos = arg_get_u32_def(ctx, 2, 0);
1615 CLIParserFree(ctx);
1616 PrintAndLogEx(INFO, "Setting orange %u blue %u", CursorCPos, CursorDPos);
1617 RepaintGraphWindow();
1618 return PM3_SUCCESS;
1621 static int CmdHexsamples(const char *Cmd) {
1623 CLIParserContext *ctx;
1624 CLIParserInit(&ctx, "data hexsamples",
1625 "Dump big buffer as hex bytes",
1626 "data hexsamples -n 128 --> dumps 128 bytes from offset 0"
1628 void *argtable[] = {
1629 arg_param_begin,
1630 arg_u64_0("b", "breaks", "<dec>", "row break, def 16"),
1631 arg_u64_0("n", NULL, "<dec>", "num of bytes to download"),
1632 arg_u64_0("o", "offset", "<hex>", "offset in big buffer"),
1633 arg_param_end
1635 CLIExecWithReturn(ctx, Cmd, argtable, false);
1636 uint32_t breaks = arg_get_u32_def(ctx, 1, 16);
1637 uint32_t requested = arg_get_u32_def(ctx, 2, 8);
1638 uint32_t offset = arg_get_u32_def(ctx, 3, 0);
1639 CLIParserFree(ctx);
1641 // sanity checks
1642 if (requested > pm3_capabilities.bigbuf_size) {
1643 requested = pm3_capabilities.bigbuf_size;
1644 PrintAndLogEx(INFO, "n is larger than big buffer size, will use %u", requested);
1647 uint8_t got[pm3_capabilities.bigbuf_size];
1648 if (offset + requested > sizeof(got)) {
1649 PrintAndLogEx(NORMAL, "Tried to read past end of buffer, <bytes %u> + <offset %u> > %d"
1650 , requested
1651 , offset
1652 , pm3_capabilities.bigbuf_size
1654 return PM3_EINVARG;
1657 if (!GetFromDevice(BIG_BUF, got, requested, offset, NULL, 0, NULL, 2500, false)) {
1658 PrintAndLogEx(WARNING, "command execution time out");
1659 return PM3_ESOFT;
1662 print_hex_break(got, requested, breaks);
1663 return PM3_SUCCESS;
1666 static int CmdHide(const char *Cmd) {
1667 CLIParserContext *ctx;
1668 CLIParserInit(&ctx, "data hide",
1669 "Show graph window",
1670 "data hide"
1672 void *argtable[] = {
1673 arg_param_begin,
1674 arg_param_end
1676 CLIExecWithReturn(ctx, Cmd, argtable, true);
1677 CLIParserFree(ctx);
1678 HideGraphWindow();
1679 return PM3_SUCCESS;
1682 // zero mean GraphBuffer
1683 int CmdHpf(const char *Cmd) {
1684 CLIParserContext *ctx;
1685 CLIParserInit(&ctx, "data hpf",
1686 "Remove DC offset from trace. It should centralize around 0",
1687 "data hpf"
1689 void *argtable[] = {
1690 arg_param_begin,
1691 arg_param_end
1693 CLIExecWithReturn(ctx, Cmd, argtable, true);
1694 CLIParserFree(ctx);
1696 uint8_t bits[GraphTraceLen];
1697 size_t size = getFromGraphBuf(bits);
1698 removeSignalOffset(bits, size);
1699 // push it back to graph
1700 setGraphBuf(bits, size);
1701 // set signal properties low/high/mean/amplitude and is_noise detection
1702 computeSignalProperties(bits, size);
1704 RepaintGraphWindow();
1705 return PM3_SUCCESS;
1708 static bool _headBit(BitstreamOut *stream) {
1709 int bytepos = stream->position >> 3; // divide by 8
1710 int bitpos = (stream->position++) & 7; // mask out 00000111
1711 return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1;
1714 static uint8_t getByte(uint8_t bits_per_sample, BitstreamOut *b) {
1715 uint8_t val = 0;
1716 for (int i = 0 ; i < bits_per_sample; i++)
1717 val |= (_headBit(b) << (7 - i));
1719 return val;
1722 int getSamples(uint32_t n, bool verbose) {
1723 return getSamplesEx(0, n, verbose);
1726 int getSamplesEx(uint32_t start, uint32_t end, bool verbose) {
1728 if (end < start) {
1729 PrintAndLogEx(WARNING, "error, end (%u) is smaller than start (%u)", end, start);
1730 return PM3_EINVARG;
1733 //If we get all but the last byte in bigbuf,
1734 // we don't have to worry about remaining trash
1735 // in the last byte in case the bits-per-sample
1736 // does not line up on byte boundaries
1737 uint8_t got[pm3_capabilities.bigbuf_size - 1];
1738 memset(got, 0x00, sizeof(got));
1740 uint32_t n = end - start;
1742 if (n == 0 || n > pm3_capabilities.bigbuf_size - 1)
1743 n = pm3_capabilities.bigbuf_size - 1;
1745 if (verbose)
1746 PrintAndLogEx(INFO, "Reading " _YELLOW_("%u") " bytes from device memory", n);
1748 PacketResponseNG response;
1749 if (!GetFromDevice(BIG_BUF, got, n, start, NULL, 0, &response, 10000, true)) {
1750 PrintAndLogEx(WARNING, "timeout while waiting for reply.");
1751 return PM3_ETIMEOUT;
1754 if (verbose) PrintAndLogEx(SUCCESS, "Data fetched");
1756 uint8_t bits_per_sample = 8;
1758 //Old devices without this feature would send 0 at arg[0]
1759 if (response.oldarg[0] > 0) {
1760 sample_config *sc = (sample_config *) response.data.asBytes;
1761 if (verbose) PrintAndLogEx(INFO, "Samples @ " _YELLOW_("%d") " bits/smpl, decimation 1:%d ", sc->bits_per_sample, sc->decimation);
1762 bits_per_sample = sc->bits_per_sample;
1765 if (bits_per_sample < 8) {
1767 if (verbose) PrintAndLogEx(INFO, "Unpacking...");
1769 BitstreamOut bout = { got, bits_per_sample * n, 0};
1770 uint32_t j = 0;
1771 for (j = 0; j * bits_per_sample < n * 8 && j < n; j++) {
1772 uint8_t sample = getByte(bits_per_sample, &bout);
1773 GraphBuffer[j] = ((int) sample) - 127;
1775 GraphTraceLen = j;
1777 if (verbose) PrintAndLogEx(INFO, "Unpacked %d samples", j);
1779 } else {
1780 for (uint32_t j = 0; j < n; j++) {
1781 GraphBuffer[j] = ((int)got[j]) - 127;
1783 GraphTraceLen = n;
1786 uint8_t bits[GraphTraceLen];
1787 size_t size = getFromGraphBuf(bits);
1788 // set signal properties low/high/mean/amplitude and is_noise detection
1789 computeSignalProperties(bits, size);
1791 setClockGrid(0, 0);
1792 DemodBufferLen = 0;
1793 RepaintGraphWindow();
1794 return PM3_SUCCESS;
1797 static int CmdSamples(const char *Cmd) {
1799 CLIParserContext *ctx;
1800 CLIParserInit(&ctx, "data samples",
1801 "Get raw samples for graph window (GraphBuffer) from device.\n"
1802 "If 0, then get whole big buffer from device.",
1803 "data samples\n"
1804 "data samples -n 10000"
1806 void *argtable[] = {
1807 arg_param_begin,
1808 arg_int0("n", NULL, "<dec>", "num of samples (512 - 40000)"),
1809 arg_lit0("v", "verbose", "verbose"),
1810 arg_param_end
1812 CLIExecWithReturn(ctx, Cmd, argtable, true);
1813 int n = arg_get_int_def(ctx, 1, 0);
1814 bool verbose = arg_get_lit(ctx, 2);
1815 CLIParserFree(ctx);
1816 return getSamples(n, verbose);
1819 int CmdTuneSamples(const char *Cmd) {
1821 CLIParserContext *ctx;
1822 CLIParserInit(&ctx, "data tune",
1823 "Measure tuning of device antenna. Results shown in graph window.\n"
1824 "This command doesn't actively tune your antennas, \n"
1825 "it's only informative by measuring voltage that the antennas will generate",
1826 "data tune"
1828 void *argtable[] = {
1829 arg_param_begin,
1830 arg_param_end
1832 CLIExecWithReturn(ctx, Cmd, argtable, true);
1833 CLIParserFree(ctx);
1835 #define NON_VOLTAGE 1000
1836 #define LF_UNUSABLE_V 2000
1837 #define LF_MARGINAL_V 10000
1838 #define HF_UNUSABLE_V 3000
1839 #define HF_MARGINAL_V 5000
1840 #define ANTENNA_ERROR 1.00 // current algo has 3% error margin.
1842 // hide demod plot line
1843 DemodBufferLen = 0;
1844 setClockGrid(0, 0);
1845 RepaintGraphWindow();
1847 int timeout = 0;
1848 int timeout_max = 20;
1849 PrintAndLogEx(INFO, "---------- " _CYAN_("Reminder") " ------------------------");
1850 PrintAndLogEx(INFO, "`" _YELLOW_("hw tune") "` doesn't actively tune your antennas,");
1851 PrintAndLogEx(INFO, "it's only informative.");
1852 PrintAndLogEx(INFO, "Measuring antenna characteristics, please wait...");
1854 clearCommandBuffer();
1855 SendCommandNG(CMD_MEASURE_ANTENNA_TUNING, NULL, 0);
1856 PacketResponseNG resp;
1857 PrintAndLogEx(INPLACE, "% 3i", timeout_max - timeout);
1858 while (!WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING, &resp, 500)) {
1859 fflush(stdout);
1860 if (timeout >= timeout_max) {
1861 PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting...");
1862 return PM3_ETIMEOUT;
1864 timeout++;
1865 PrintAndLogEx(INPLACE, "% 3i", timeout_max - timeout);
1868 if (resp.status != PM3_SUCCESS) {
1869 PrintAndLogEx(WARNING, "Antenna tuning failed");
1870 return PM3_ESOFT;
1873 PrintAndLogEx(NORMAL, "");
1874 PrintAndLogEx(INFO, "---------- " _CYAN_("LF Antenna") " ----------");
1875 // in mVolt
1876 struct p {
1877 uint32_t v_lf134;
1878 uint32_t v_lf125;
1879 uint32_t v_lfconf;
1880 uint32_t v_hf;
1881 uint32_t peak_v;
1882 uint32_t peak_f;
1883 int divisor;
1884 uint8_t results[256];
1885 } PACKED;
1887 struct p *package = (struct p *)resp.data.asBytes;
1889 if (package->v_lf125 > NON_VOLTAGE)
1890 PrintAndLogEx(SUCCESS, "LF antenna: %5.2f V - %.2f kHz", (package->v_lf125 * ANTENNA_ERROR) / 1000.0, LF_DIV2FREQ(LF_DIVISOR_125));
1892 if (package->v_lf134 > NON_VOLTAGE)
1893 PrintAndLogEx(SUCCESS, "LF antenna: %5.2f V - %.2f kHz", (package->v_lf134 * ANTENNA_ERROR) / 1000.0, LF_DIV2FREQ(LF_DIVISOR_134));
1895 if (package->v_lfconf > NON_VOLTAGE && package->divisor > 0 && package->divisor != LF_DIVISOR_125 && package->divisor != LF_DIVISOR_134)
1896 PrintAndLogEx(SUCCESS, "LF antenna: %5.2f V - %.2f kHz", (package->v_lfconf * ANTENNA_ERROR) / 1000.0, LF_DIV2FREQ(package->divisor));
1898 if (package->peak_v > NON_VOLTAGE && package->peak_f > 0)
1899 PrintAndLogEx(SUCCESS, "LF optimal: %5.2f V - %6.2f kHz", (package->peak_v * ANTENNA_ERROR) / 1000.0, LF_DIV2FREQ(package->peak_f));
1901 // Empirical measures in mV
1902 const double vdd_rdv4 = 9000;
1903 const double vdd_other = 5400;
1904 double vdd = IfPm3Rdv4Fw() ? vdd_rdv4 : vdd_other;
1906 if (package->peak_v > NON_VOLTAGE && package->peak_f > 0) {
1908 // Q measure with Q=f/delta_f
1909 double v_3db_scaled = (double)(package->peak_v * 0.707) / 512; // /512 == >>9
1910 uint32_t s2 = 0, s4 = 0;
1911 for (int i = 1; i < 256; i++) {
1912 if ((s2 == 0) && (package->results[i] > v_3db_scaled)) {
1913 s2 = i;
1915 if ((s2 != 0) && (package->results[i] < v_3db_scaled)) {
1916 s4 = i;
1917 break;
1920 double lfq1 = 0;
1921 if (s4 != 0) { // we got all our points of interest
1922 double a = package->results[s2 - 1];
1923 double b = package->results[s2];
1924 double f1 = LF_DIV2FREQ(s2 - 1 + (v_3db_scaled - a) / (b - a));
1925 double c = package->results[s4 - 1];
1926 double d = package->results[s4];
1927 double f2 = LF_DIV2FREQ(s4 - 1 + (c - v_3db_scaled) / (c - d));
1928 lfq1 = LF_DIV2FREQ(package->peak_f) / (f1 - f2);
1929 PrintAndLogEx(SUCCESS, "Approx. Q factor (*): %.1lf by frequency bandwidth measurement", lfq1);
1932 // Q measure with Vlr=Q*(2*Vdd/pi)
1933 double lfq2 = (double)package->peak_v * 3.14 / 2 / vdd;
1934 PrintAndLogEx(SUCCESS, "Approx. Q factor (*): %.1lf by peak voltage measurement", lfq2);
1935 // cross-check results
1936 if (lfq1 > 3) {
1937 double approx_vdd = (double)package->peak_v * 3.14 / 2 / lfq1;
1938 // Got 8858 on a RDV4 with large antenna 134/14
1939 // Got 8761 on a non-RDV4
1940 const double approx_vdd_other_max = 8840;
1942 // 1% over threshold and supposedly non-RDV4
1943 if ((approx_vdd > approx_vdd_other_max * 1.01) && (!IfPm3Rdv4Fw())) {
1944 PrintAndLogEx(WARNING, "Contradicting measures seem to indicate you're running a " _YELLOW_("PM3_GENERIC firmware on a RDV4"));
1945 PrintAndLogEx(WARNING, "False positives is possible but please check your setup");
1947 // 1% below threshold and supposedly RDV4
1948 if ((approx_vdd < approx_vdd_other_max * 0.99) && (IfPm3Rdv4Fw())) {
1949 PrintAndLogEx(WARNING, "Contradicting measures seem to indicate you're running a " _YELLOW_("PM3_RDV4 firmware on a generic device"));
1950 PrintAndLogEx(WARNING, "False positives is possible but please check your setup");
1955 char judgement[20];
1956 memset(judgement, 0, sizeof(judgement));
1957 // LF evaluation
1958 if (package->peak_v < LF_UNUSABLE_V)
1959 sprintf(judgement, _RED_("UNUSABLE"));
1960 else if (package->peak_v < LF_MARGINAL_V)
1961 sprintf(judgement, _YELLOW_("MARGINAL"));
1962 else
1963 sprintf(judgement, _GREEN_("OK"));
1965 PrintAndLogEx((package->peak_v < LF_UNUSABLE_V) ? WARNING : SUCCESS, "LF antenna is %s", judgement);
1967 PrintAndLogEx(INFO, "---------- " _CYAN_("HF Antenna") " ----------");
1968 // HF evaluation
1969 if (package->v_hf > NON_VOLTAGE)
1970 PrintAndLogEx(SUCCESS, "HF antenna: %5.2f V - 13.56 MHz", (package->v_hf * ANTENNA_ERROR) / 1000.0);
1972 memset(judgement, 0, sizeof(judgement));
1974 if (package->v_hf >= HF_UNUSABLE_V) {
1975 // Q measure with Vlr=Q*(2*Vdd/pi)
1976 double hfq = (double)package->v_hf * 3.14 / 2 / vdd;
1977 PrintAndLogEx(SUCCESS, "Approx. Q factor (*): %.1lf by peak voltage measurement", hfq);
1979 if (package->v_hf < HF_UNUSABLE_V)
1980 sprintf(judgement, _RED_("UNUSABLE"));
1981 else if (package->v_hf < HF_MARGINAL_V)
1982 sprintf(judgement, _YELLOW_("MARGINAL"));
1983 else
1984 sprintf(judgement, _GREEN_("OK"));
1986 PrintAndLogEx((package->v_hf < HF_UNUSABLE_V) ? WARNING : SUCCESS, "HF antenna is %s", judgement);
1987 PrintAndLogEx(NORMAL, "\n(*) Q factor must be measured without tag on the antenna");
1989 // graph LF measurements
1990 // even here, these values has 3% error.
1991 uint16_t test1 = 0;
1992 for (int i = 0; i < 256; i++) {
1993 GraphBuffer[i] = package->results[i] - 128;
1994 test1 += package->results[i];
1997 if (test1 > 0) {
1998 PrintAndLogEx(SUCCESS, "\nDisplaying LF tuning graph. Divisor %d (blue) is %.2f kHz, %d (red) is %.2f kHz.\n\n",
1999 LF_DIVISOR_134, LF_DIV2FREQ(LF_DIVISOR_134), LF_DIVISOR_125, LF_DIV2FREQ(LF_DIVISOR_125));
2000 GraphTraceLen = 256;
2001 CursorCPos = LF_DIVISOR_125;
2002 CursorDPos = LF_DIVISOR_134;
2003 ShowGraphWindow();
2004 RepaintGraphWindow();
2005 } else {
2007 PrintAndLogEx(FAILED, "\nNot showing LF tuning graph since all values is zero.\n\n");
2010 return PM3_SUCCESS;
2013 static int CmdLoad(const char *Cmd) {
2015 CLIParserContext *ctx;
2016 CLIParserInit(&ctx, "data load",
2017 "This command loads the contents of a pm3 file into graph window\n",
2018 "data load -f myfilename"
2021 void *argtable[] = {
2022 arg_param_begin,
2023 arg_str1("f", "file", "<fn>", "file to load"),
2024 arg_param_end
2026 CLIExecWithReturn(ctx, Cmd, argtable, false);
2028 int fnlen = 0;
2029 char filename[FILE_PATH_SIZE] = {0};
2030 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
2031 CLIParserFree(ctx);
2033 char *path = NULL;
2034 if (searchFile(&path, TRACES_SUBDIR, filename, ".pm3", true) != PM3_SUCCESS) {
2035 if (searchFile(&path, TRACES_SUBDIR, filename, "", false) != PM3_SUCCESS) {
2036 return PM3_EFILE;
2040 FILE *f = fopen(path, "r");
2041 if (!f) {
2042 PrintAndLogEx(WARNING, "couldn't open '%s'", path);
2043 free(path);
2044 return PM3_EFILE;
2046 free(path);
2048 GraphTraceLen = 0;
2049 char line[80];
2050 while (fgets(line, sizeof(line), f)) {
2051 GraphBuffer[GraphTraceLen] = atoi(line);
2052 GraphTraceLen++;
2054 if (GraphTraceLen >= MAX_GRAPH_TRACE_LEN)
2055 break;
2057 fclose(f);
2059 PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " samples", GraphTraceLen);
2061 uint8_t bits[GraphTraceLen];
2062 size_t size = getFromGraphBuf(bits);
2064 removeSignalOffset(bits, size);
2065 setGraphBuf(bits, size);
2066 computeSignalProperties(bits, size);
2068 setClockGrid(0, 0);
2069 DemodBufferLen = 0;
2070 RepaintGraphWindow();
2071 return PM3_SUCCESS;
2074 // trim graph from the end
2075 int CmdLtrim(const char *Cmd) {
2076 CLIParserContext *ctx;
2077 CLIParserInit(&ctx, "data ltrim",
2078 "Trim samples from left of trace",
2079 "data ltrim -i 300 --> keep 300 - end"
2081 void *argtable[] = {
2082 arg_param_begin,
2083 arg_u64_1("i", "idx", "<dec>", "from index to beginning trace"),
2084 arg_param_end
2086 CLIExecWithReturn(ctx, Cmd, argtable, false);
2087 uint32_t ds = arg_get_u32(ctx, 1);
2088 CLIParserFree(ctx);
2090 // sanitycheck
2091 if (GraphTraceLen <= ds) {
2092 PrintAndLogEx(WARNING, "index out of bounds");
2093 return PM3_EINVARG;
2096 for (uint32_t i = ds; i < GraphTraceLen; ++i)
2097 GraphBuffer[i - ds] = GraphBuffer[i];
2099 GraphTraceLen -= ds;
2100 g_DemodStartIdx -= ds;
2101 RepaintGraphWindow();
2102 return PM3_SUCCESS;
2105 // trim graph from the beginning
2106 static int CmdRtrim(const char *Cmd) {
2108 CLIParserContext *ctx;
2109 CLIParserInit(&ctx, "data rtrim",
2110 "Trim samples from right of trace",
2111 "data rtrim -i 4000 --> keep 0 - 4000"
2113 void *argtable[] = {
2114 arg_param_begin,
2115 arg_u64_1("i", "idx", "<dec>", "from index to end trace"),
2116 arg_param_end
2118 CLIExecWithReturn(ctx, Cmd, argtable, false);
2119 uint32_t ds = arg_get_u32(ctx, 1);
2120 CLIParserFree(ctx);
2122 // sanitycheck
2123 if (GraphTraceLen <= ds) {
2124 PrintAndLogEx(WARNING, "index out of bounds");
2125 return PM3_EINVARG;
2128 GraphTraceLen = ds;
2129 RepaintGraphWindow();
2130 return PM3_SUCCESS;
2133 // trim graph (middle) piece
2134 static int CmdMtrim(const char *Cmd) {
2136 CLIParserContext *ctx;
2137 CLIParserInit(&ctx, "data mtrim",
2138 "Trim out samples from the specified start to the specified end point",
2139 "data mtrim -s 1000 -e 2000 --> keep between 1000 and 2000"
2141 void *argtable[] = {
2142 arg_param_begin,
2143 arg_u64_1("s", "start", "<dec>", "start point"),
2144 arg_u64_1("e", "end", "<dec>", "end point"),
2145 arg_param_end
2147 CLIExecWithReturn(ctx, Cmd, argtable, false);
2148 uint32_t start = arg_get_u32(ctx, 1);
2149 uint32_t stop = arg_get_u32(ctx, 2);
2150 CLIParserFree(ctx);
2152 if (start > GraphTraceLen || stop > GraphTraceLen || start >= stop) {
2153 PrintAndLogEx(WARNING, "start and end points doesn't align");
2154 return PM3_EINVARG;
2157 // leave start position sample
2158 start++;
2160 GraphTraceLen = stop - start;
2161 for (uint32_t i = 0; i < GraphTraceLen; i++) {
2162 GraphBuffer[i] = GraphBuffer[start + i];
2165 return PM3_SUCCESS;
2168 int CmdNorm(const char *Cmd) {
2170 CLIParserContext *ctx;
2171 CLIParserInit(&ctx, "data norm",
2172 "Normalize max/min to +/-128",
2173 "data norm"
2175 void *argtable[] = {
2176 arg_param_begin,
2177 arg_param_end
2179 CLIExecWithReturn(ctx, Cmd, argtable, true);
2180 CLIParserFree(ctx);
2182 int max = INT_MIN, min = INT_MAX;
2184 // Find local min, max
2185 for (uint32_t i = 10; i < GraphTraceLen; ++i) {
2186 if (GraphBuffer[i] > max) max = GraphBuffer[i];
2187 if (GraphBuffer[i] < min) min = GraphBuffer[i];
2190 if (max != min) {
2191 for (uint32_t i = 0; i < GraphTraceLen; ++i) {
2192 GraphBuffer[i] = ((long)(GraphBuffer[i] - ((max + min) / 2)) * 256) / (max - min);
2193 //marshmelow: adjusted *1000 to *256 to make +/- 128 so demod commands still work
2197 uint8_t bits[GraphTraceLen];
2198 size_t size = getFromGraphBuf(bits);
2199 // set signal properties low/high/mean/amplitude and is_noise detection
2200 computeSignalProperties(bits, size);
2202 RepaintGraphWindow();
2203 return PM3_SUCCESS;
2206 int CmdPlot(const char *Cmd) {
2207 CLIParserContext *ctx;
2208 CLIParserInit(&ctx, "data plot",
2209 "Show graph window \n"
2210 "hit 'h' in window for detail keystroke help available",
2211 "data plot"
2213 void *argtable[] = {
2214 arg_param_begin,
2215 arg_param_end
2217 CLIExecWithReturn(ctx, Cmd, argtable, true);
2218 CLIParserFree(ctx);
2219 ShowGraphWindow();
2220 return PM3_SUCCESS;
2223 int CmdSave(const char *Cmd) {
2225 CLIParserContext *ctx;
2226 CLIParserInit(&ctx, "data save",
2227 "Save trace from graph window , i.e. the GraphBuffer\n"
2228 "This is a text file with number -127 to 127. With the option `w` you can save it as wave file\n"
2229 "Filename should be without file extension",
2230 "data save -f myfilename -> save graph buffer to file\n"
2231 "data save --wave -f myfilename -> save graph buffer to wave file"
2234 void *argtable[] = {
2235 arg_param_begin,
2236 arg_lit0("w", "wave", "save as wave format (.wav)"),
2237 arg_str1("f", "file", "<fn w/o ext>", "save file name"),
2238 arg_param_end
2240 CLIExecWithReturn(ctx, Cmd, argtable, false);
2242 bool as_wave = arg_get_lit(ctx, 1);
2244 int fnlen = 0;
2245 char filename[FILE_PATH_SIZE] = {0};
2246 // CLIGetStrWithReturn(ctx, 2, (uint8_t *)filename, &fnlen);
2247 CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
2249 CLIParserFree(ctx);
2251 if (as_wave)
2252 return saveFileWAVE(filename, GraphBuffer, GraphTraceLen);
2253 else
2254 return saveFilePM3(filename, GraphBuffer, GraphTraceLen);
2257 static int CmdTimeScale(const char *Cmd) {
2259 CLIParserContext *ctx;
2260 CLIParserInit(&ctx, "data timescale",
2261 "Set cursor display timescale.\n"
2262 "Setting the timescale makes the differential `dt` reading between the yellow and purple markers meaningful.\n"
2263 "once the timescale is set, the differential reading between brackets can become a time duration.",
2264 "data timescale --sr 125 -u ms -> for LF sampled at 125 kHz. Reading will be in milliseconds\n"
2265 "data timescale --sr 1.695 -u us -> for HF sampled at 16 * fc/128. Reading will be in microseconds\n"
2266 "data timescale --sr 16 -u ETU -> for HF with 16 samples per ETU (fc/128). Reading will be in ETUs"
2268 void *argtable[] = {
2269 arg_param_begin,
2270 arg_dbl1(NULL, "sr", "<float>", "sets timescale factor according to sampling rate"),
2271 arg_str0("u", "unit", "<string>", "time unit to display (max 10 chars)"),
2272 arg_param_end
2274 CLIExecWithReturn(ctx, Cmd, argtable, false);
2275 CursorScaleFactor = arg_get_dbl_def(ctx, 1, 1);
2276 if (CursorScaleFactor <= 0) {
2277 PrintAndLogEx(FAILED, "bad, can't have negative or zero timescale factor");
2278 CursorScaleFactor = 1;
2280 int len = 0;
2281 CursorScaleFactorUnit[0] = '\x00';
2282 CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)CursorScaleFactorUnit, sizeof(CursorScaleFactorUnit), &len);
2283 CLIParserFree(ctx);
2284 RepaintGraphWindow();
2285 return PM3_SUCCESS;
2288 int directionalThreshold(const int *in, int *out, size_t len, int8_t up, int8_t down) {
2290 int lastValue = in[0];
2292 // Will be changed at the end, but init 0 as we adjust to last samples
2293 // value if no threshold kicks in.
2294 out[0] = 0;
2296 for (size_t i = 1; i < len; ++i) {
2297 // Apply first threshold to samples heading up
2298 if (in[i] >= up && in[i] > lastValue) {
2299 lastValue = out[i]; // Buffer last value as we overwrite it.
2300 out[i] = 1;
2302 // Apply second threshold to samples heading down
2303 else if (in[i] <= down && in[i] < lastValue) {
2304 lastValue = out[i]; // Buffer last value as we overwrite it.
2305 out[i] = -1;
2306 } else {
2307 lastValue = out[i]; // Buffer last value as we overwrite it.
2308 out[i] = out[i - 1];
2312 // Align with first edited sample.
2313 out[0] = out[1];
2314 return PM3_SUCCESS;
2317 static int CmdDirectionalThreshold(const char *Cmd) {
2318 CLIParserContext *ctx;
2319 CLIParserInit(&ctx, "data dirthreshold",
2320 "Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev.",
2321 "data dirthreshold -u 10 -d -10"
2323 void *argtable[] = {
2324 arg_param_begin,
2325 arg_int1("d", "down", "<dec>", "threshold down"),
2326 arg_int1("u", "up", "<dec>", "threshold up"),
2327 arg_param_end
2329 CLIExecWithReturn(ctx, Cmd, argtable, false);
2330 int8_t down = arg_get_int(ctx, 1);
2331 int8_t up = arg_get_int(ctx, 2);
2332 CLIParserFree(ctx);
2334 PrintAndLogEx(INFO, "Applying up threshold: " _YELLOW_("%i") ", down threshold: " _YELLOW_("%i") "\n", up, down);
2336 directionalThreshold(GraphBuffer, GraphBuffer, GraphTraceLen, up, down);
2338 // set signal properties low/high/mean/amplitude and isnoice detection
2339 uint8_t bits[GraphTraceLen];
2340 size_t size = getFromGraphBuf(bits);
2341 // set signal properties low/high/mean/amplitude and is_noice detection
2342 computeSignalProperties(bits, size);
2344 RepaintGraphWindow();
2345 return PM3_SUCCESS;
2348 static int CmdZerocrossings(const char *Cmd) {
2349 CLIParserContext *ctx;
2350 CLIParserInit(&ctx, "data zerocrossings",
2351 "Count time between zero-crossings",
2352 "data zerocrossings"
2354 void *argtable[] = {
2355 arg_param_begin,
2356 arg_param_end
2358 CLIExecWithReturn(ctx, Cmd, argtable, true);
2359 CLIParserFree(ctx);
2361 // Zero-crossings aren't meaningful unless the signal is zero-mean.
2362 CmdHpf("");
2364 int sign = 1, zc = 0, lastZc = 0;
2366 for (uint32_t i = 0; i < GraphTraceLen; ++i) {
2367 if (GraphBuffer[i] * sign >= 0) {
2368 // No change in sign, reproduce the previous sample count.
2369 zc++;
2370 GraphBuffer[i] = lastZc;
2371 } else {
2372 // Change in sign, reset the sample count.
2373 sign = -sign;
2374 GraphBuffer[i] = lastZc;
2375 if (sign > 0) {
2376 lastZc = zc;
2377 zc = 0;
2382 uint8_t bits[GraphTraceLen];
2383 size_t size = getFromGraphBuf(bits);
2384 // set signal properties low/high/mean/amplitude and is_noise detection
2385 computeSignalProperties(bits, size);
2386 RepaintGraphWindow();
2387 return PM3_SUCCESS;
2391 * @brief Utility for conversion via cmdline.
2392 * @param Cmd
2393 * @return
2395 static int Cmdbin2hex(const char *Cmd) {
2397 CLIParserContext *ctx;
2398 CLIParserInit(&ctx, "data bin2hex",
2399 "This function converts binary to hexadecimal. It will ignore all\n"
2400 "characters not 1 or 0 but stop reading on whitespace",
2401 "data bin2hex -d 0101111001010"
2403 void *argtable[] = {
2404 arg_param_begin,
2405 arg_strx0("d", "data", "<bin>", "binary string to convert"),
2406 arg_param_end
2408 CLIExecWithReturn(ctx, Cmd, argtable, false);
2409 int blen = 0;
2410 uint8_t binarr[400] = {0x00};
2411 int res = CLIParamBinToBuf(arg_get_str(ctx, 1), binarr, sizeof(binarr), &blen);
2412 CLIParserFree(ctx);
2414 if (res) {
2415 PrintAndLogEx(FAILED, "Error parsing binary string");
2416 return PM3_EINVARG;
2419 // Number of digits supplied as argument
2420 size_t bytelen = (blen + 7) / 8;
2421 uint8_t *arr = (uint8_t *) calloc(bytelen, sizeof(uint8_t));
2422 memset(arr, 0, bytelen);
2423 BitstreamOut bout = { arr, 0, 0 };
2425 for (int i = 0; i < blen; i++) {
2426 uint8_t c = binarr[i];
2427 if (c == 1)
2428 pushBit(&bout, 1);
2429 else if (c == 0)
2430 pushBit(&bout, 0);
2431 else
2432 PrintAndLogEx(INFO, "Ignoring '%d' at pos %d", c, i);
2435 if (bout.numbits % 8 != 0)
2436 PrintAndLogEx(INFO, "[right padded with %d zeroes]", 8 - (bout.numbits % 8));
2438 PrintAndLogEx(SUCCESS, _YELLOW_("%s"), sprint_hex(arr, bytelen));
2439 free(arr);
2440 return PM3_SUCCESS;
2443 static int Cmdhex2bin(const char *Cmd) {
2444 CLIParserContext *ctx;
2445 CLIParserInit(&ctx, "data hex2bin",
2446 "This function converts hexadecimal to binary. It will ignore all\n"
2447 "non-hexadecimal characters but stop reading on whitespace",
2448 "data hex2bin -d 01020304"
2450 void *argtable[] = {
2451 arg_param_begin,
2452 arg_str0("d", "data", "<hex>", "bytes to convert"),
2453 arg_param_end
2455 CLIExecWithReturn(ctx, Cmd, argtable, false);
2456 int dlen = 0;
2457 char data[200] = {0x00};
2458 int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)data, sizeof(data), &dlen);
2459 CLIParserFree(ctx);
2461 if (res) {
2462 PrintAndLogEx(FAILED, "Error parsing bytes");
2463 return PM3_EINVARG;
2466 for (int i = 0; i < dlen; i++) {
2467 char x = data[i];
2468 if (isxdigit(x) == false) {
2469 PrintAndLogEx(ERR, "Non hex digit found");
2470 return PM3_EINVARG;
2474 PrintAndLogEx(SUCCESS, "" NOLF);
2475 for (int i = 0; i < dlen; i++) {
2476 char x = data[i];
2478 // capitalize
2479 if (x >= 'a' && x <= 'f')
2480 x -= 32;
2481 // convert to numeric value
2482 if (x >= '0' && x <= '9')
2483 x -= '0';
2484 else if (x >= 'A' && x <= 'F')
2485 x -= 'A' - 10;
2486 else
2487 continue;
2489 for (int j = 0 ; j < 4 ; ++j) {
2490 PrintAndLogEx(NORMAL, "%d" NOLF, (x >> (3 - j)) & 1);
2493 PrintAndLogEx(NORMAL, "");
2494 return PM3_SUCCESS;
2497 /* // example of FSK2 RF/50 Tones
2498 static const int LowTone[] = {
2499 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,
2500 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,
2501 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,
2502 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,
2503 1, 1, 1, 1, 1, -1, -1, -1, -1, -1
2505 static const int HighTone[] = {
2506 1, 1, 1, 1, 1, -1, -1, -1, -1, // note one extra 1 to padd due to 50/8 remainder (1/2 the remainder)
2507 1, 1, 1, 1, -1, -1, -1, -1,
2508 1, 1, 1, 1, -1, -1, -1, -1,
2509 1, 1, 1, 1, -1, -1, -1, -1,
2510 1, 1, 1, 1, -1, -1, -1, -1,
2511 1, 1, 1, 1, -1, -1, -1, -1, -1, // note one extra -1 to padd due to 50/8 remainder
2514 static void GetHiLoTone(int *LowTone, int *HighTone, int clk, int LowToneFC, int HighToneFC) {
2515 int i, j = 0;
2516 int Left_Modifier = ((clk % LowToneFC) % 2) + ((clk % LowToneFC) / 2);
2517 int Right_Modifier = (clk % LowToneFC) / 2;
2518 //int HighToneMod = clk mod HighToneFC;
2519 int LeftHalfFCCnt = (LowToneFC % 2) + (LowToneFC / 2); //truncate
2520 int FCs_per_clk = clk / LowToneFC;
2522 // need to correctly split up the clock to field clocks.
2523 // First attempt uses modifiers on each end to make up for when FCs don't evenly divide into Clk
2525 // start with LowTone
2526 // set extra 1 modifiers to make up for when FC doesn't divide evenly into Clk
2527 for (i = 0; i < Left_Modifier; i++) {
2528 LowTone[i] = 1;
2531 // loop # of field clocks inside the main clock
2532 for (i = 0; i < (FCs_per_clk); i++) {
2533 // loop # of samples per field clock
2534 for (j = 0; j < LowToneFC; j++) {
2535 LowTone[(i * LowToneFC) + Left_Modifier + j] = (j < LeftHalfFCCnt) ? 1 : -1;
2539 int k;
2540 // add last -1 modifiers
2541 for (k = 0; k < Right_Modifier; k++) {
2542 LowTone[((i - 1) * LowToneFC) + Left_Modifier + j + k] = -1;
2545 // now do hightone
2546 Left_Modifier = ((clk % HighToneFC) % 2) + ((clk % HighToneFC) / 2);
2547 Right_Modifier = (clk % HighToneFC) / 2;
2548 LeftHalfFCCnt = (HighToneFC % 2) + (HighToneFC / 2); //truncate
2549 FCs_per_clk = clk / HighToneFC;
2551 for (i = 0; i < Left_Modifier; i++) {
2552 HighTone[i] = 1;
2555 // loop # of field clocks inside the main clock
2556 for (i = 0; i < (FCs_per_clk); i++) {
2557 // loop # of samples per field clock
2558 for (j = 0; j < HighToneFC; j++) {
2559 HighTone[(i * HighToneFC) + Left_Modifier + j] = (j < LeftHalfFCCnt) ? 1 : -1;
2563 // add last -1 modifiers
2564 for (k = 0; k < Right_Modifier; k++) {
2565 PrintAndLogEx(NORMAL, "(i-1)*HighToneFC+lm+j+k %i", ((i - 1) * HighToneFC) + Left_Modifier + j + k);
2566 HighTone[((i - 1) * HighToneFC) + Left_Modifier + j + k] = -1;
2568 if (g_debugMode == 2) {
2569 for (i = 0; i < clk; i++) {
2570 PrintAndLogEx(NORMAL, "Low: %i, High: %i", LowTone[i], HighTone[i]);
2575 //old CmdFSKdemod adapted by marshmellow
2576 //converts FSK to clear NRZ style wave. (or demodulates)
2577 static int FSKToNRZ(int *data, size_t *dataLen, uint8_t clk, uint8_t LowToneFC, uint8_t HighToneFC) {
2578 uint8_t ans = 0;
2579 if (clk == 0 || LowToneFC == 0 || HighToneFC == 0) {
2580 int firstClockEdge = 0;
2581 ans = fskClocks((uint8_t *) &LowToneFC, (uint8_t *) &HighToneFC, (uint8_t *) &clk, &firstClockEdge);
2582 if (g_debugMode > 1) {
2583 PrintAndLogEx(NORMAL, "DEBUG FSKtoNRZ: detected clocks: fc_low %i, fc_high %i, clk %i, firstClockEdge %i, ans %u", LowToneFC, HighToneFC, clk, firstClockEdge, ans);
2586 // currently only know fsk modulations with field clocks < 10 samples and > 4 samples. filter out to remove false positives (and possibly destroying ask/psk modulated waves...)
2587 if (ans == 0 || clk == 0 || LowToneFC == 0 || HighToneFC == 0 || LowToneFC > 10 || HighToneFC < 4) {
2588 if (g_debugMode > 1) {
2589 PrintAndLogEx(NORMAL, "DEBUG FSKtoNRZ: no fsk clocks found");
2591 return PM3_ESOFT;
2594 int LowTone[clk];
2595 int HighTone[clk];
2596 GetHiLoTone(LowTone, HighTone, clk, LowToneFC, HighToneFC);
2598 // loop through ([all samples] - clk)
2599 for (size_t i = 0; i < *dataLen - clk; ++i) {
2600 int lowSum = 0, highSum = 0;
2602 // sum all samples together starting from this sample for [clk] samples for each tone (multiply tone value with sample data)
2603 for (size_t j = 0; j < clk; ++j) {
2604 lowSum += LowTone[j] * data[i + j];
2605 highSum += HighTone[j] * data[i + j];
2607 // get abs( [average sample value per clk] * 100 ) (or a rolling average of sorts)
2608 lowSum = abs(100 * lowSum / clk);
2609 highSum = abs(100 * highSum / clk);
2610 // save these back to buffer for later use
2611 data[i] = (highSum << 16) | lowSum;
2614 // now we have the abs( [average sample value per clk] * 100 ) for each tone
2615 // loop through again [all samples] - clk - 16
2616 // note why 16??? is 16 the largest FC? changed to LowToneFC as that should be the > fc
2617 for (size_t i = 0; i < *dataLen - clk - LowToneFC; ++i) {
2618 int lowTot = 0, highTot = 0;
2620 // sum a field clock width of abs( [average sample values per clk] * 100) for each tone
2621 for (size_t j = 0; j < LowToneFC; ++j) { //10 for fsk2
2622 lowTot += (data[i + j] & 0xffff);
2624 for (size_t j = 0; j < HighToneFC; j++) { //8 for fsk2
2625 highTot += (data[i + j] >> 16);
2628 // subtract the sum of lowTone averages by the sum of highTone averages as it
2629 // and write back the new graph value
2630 data[i] = lowTot - highTot;
2632 // update dataLen to what we put back to the data sample buffer
2633 *dataLen -= (clk + LowToneFC);
2634 return PM3_SUCCESS;
2637 static int CmdFSKToNRZ(const char *Cmd) {
2639 CLIParserContext *ctx;
2640 CLIParserInit(&ctx, "data fsktonrz",
2641 "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)\n"
2642 "Omitted values are autodetect instead",
2643 "data fsktonrz\n"
2644 "data fsktonrz -c 32 --low 8 --hi 10");
2646 void *argtable[] = {
2647 arg_param_begin,
2648 arg_int0("c", "clk", "<dec>", "clock"),
2649 arg_int0(NULL, "low", "<dec>", "low field clock"),
2650 arg_int0(NULL, "hi", "<dec>", "high field clock"),
2651 arg_param_end
2653 CLIExecWithReturn(ctx, Cmd, argtable, true);
2655 int clk = arg_get_int_def(ctx, 1, 0);
2656 int fc_low = arg_get_int_def(ctx, 2, 0);
2657 int fc_high = arg_get_int_def(ctx, 3, 0);
2658 CLIParserFree(ctx);
2660 setClockGrid(0, 0);
2661 DemodBufferLen = 0;
2662 int ans = FSKToNRZ(GraphBuffer, &GraphTraceLen, clk, fc_low, fc_high);
2663 CmdNorm("");
2664 RepaintGraphWindow();
2665 return ans;
2668 static int CmdDataIIR(const char *Cmd) {
2670 CLIParserContext *ctx;
2671 CLIParserInit(&ctx, "data iir",
2672 "Apply IIR buttersworth filter on plot data",
2673 "data iir -n 2"
2675 void *argtable[] = {
2676 arg_param_begin,
2677 arg_u64_1("n", NULL, "<dec>", "factor n"),
2678 arg_param_end
2680 CLIExecWithReturn(ctx, Cmd, argtable, false);
2681 uint8_t k = (arg_get_u32_def(ctx, 1, 0) & 0xFF);
2682 CLIParserFree(ctx);
2684 iceSimple_Filter(GraphBuffer, GraphTraceLen, k);
2686 uint8_t bits[GraphTraceLen];
2687 size_t size = getFromGraphBuf(bits);
2688 // set signal properties low/high/mean/amplitude and is_noise detection
2689 computeSignalProperties(bits, size);
2690 RepaintGraphWindow();
2691 return PM3_SUCCESS;
2694 typedef struct {
2695 t55xx_modulation modulation;
2696 int bitrate;
2697 int carrier;
2698 uint8_t fc1;
2699 uint8_t fc2;
2700 } lf_modulation_t;
2702 static int print_modulation(lf_modulation_t b) {
2703 PrintAndLogEx(INFO, " Modulation........ " _GREEN_("%s"), GetSelectedModulationStr(b.modulation));
2704 PrintAndLogEx(INFO, " Bit clock......... " _GREEN_("RF/%d"), b.bitrate);
2705 PrintAndLogEx(INFO, " Approx baudrate... " _GREEN_("%.f") " baud", (125000 / (float)b.bitrate));
2706 switch (b.modulation) {
2707 case DEMOD_PSK1:
2708 case DEMOD_PSK2:
2709 case DEMOD_PSK3:
2710 PrintAndLogEx(SUCCESS, " Carrier rate...... %d", b.carrier);
2711 break;
2712 case DEMOD_FSK:
2713 case DEMOD_FSK1:
2714 case DEMOD_FSK1a:
2715 case DEMOD_FSK2:
2716 case DEMOD_FSK2a:
2717 PrintAndLogEx(SUCCESS, " Field Clocks...... FC/%u, FC/%u", b.fc1, b.fc2);
2718 break;
2719 case DEMOD_NRZ:
2720 case DEMOD_ASK:
2721 case DEMOD_BI:
2722 case DEMOD_BIa:
2723 default:
2724 break;
2726 PrintAndLogEx(NORMAL, "");
2727 return PM3_SUCCESS;
2730 static int try_detect_modulation(void) {
2732 lf_modulation_t tests[6];
2733 int clk = 0, firstClockEdge = 0;
2734 uint8_t hits = 0, ans = 0;
2735 uint8_t fc1 = 0, fc2 = 0;
2736 bool st = false;
2738 ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, &firstClockEdge);
2740 if (ans && ((fc1 == 10 && fc2 == 8) || (fc1 == 8 && fc2 == 5))) {
2742 if ((FSKrawDemod(0, 0, 0, 0, false) == PM3_SUCCESS)) {
2743 tests[hits].modulation = DEMOD_FSK;
2744 if (fc1 == 8 && fc2 == 5) {
2745 tests[hits].modulation = DEMOD_FSK1a;
2746 } else if (fc1 == 10 && fc2 == 8) {
2747 tests[hits].modulation = DEMOD_FSK2;
2750 tests[hits].bitrate = clk;
2751 tests[hits].fc1 = fc1;
2752 tests[hits].fc2 = fc2;
2753 ++hits;
2756 } else {
2757 clk = GetAskClock("", false);
2758 if (clk > 0) {
2759 // 0 = auto clock
2760 // 0 = no invert
2761 // 1 = maxError 1
2762 // 0 = max len
2763 // false = no amplify
2764 // false = no verbose
2765 // false = no emSearch
2766 // 1 = Ask/Man
2767 // st = true
2768 if ((ASKDemod_ext(0, 0, 1, 0, false, false, false, 1, &st) == PM3_SUCCESS)) {
2769 tests[hits].modulation = DEMOD_ASK;
2770 tests[hits].bitrate = clk;
2771 ++hits;
2773 // "0 0 1 " == clock auto, invert true, maxError 1.
2774 // false = no verbose
2775 // false = no emSearch
2776 // 1 = Ask/Man
2777 // st = true
2779 // ASK / biphase
2780 if ((ASKbiphaseDemod(0, 0, 0, 2, false) == PM3_SUCCESS)) {
2781 tests[hits].modulation = DEMOD_BI;
2782 tests[hits].bitrate = clk;
2783 ++hits;
2785 // ASK / Diphase
2786 if ((ASKbiphaseDemod(0, 0, 1, 2, false) == PM3_SUCCESS)) {
2787 tests[hits].modulation = DEMOD_BIa;
2788 tests[hits].bitrate = clk;
2789 ++hits;
2792 clk = GetNrzClock("", false);
2793 if ((NRZrawDemod(0, 0, 1, false) == PM3_SUCCESS)) {
2794 tests[hits].modulation = DEMOD_NRZ;
2795 tests[hits].bitrate = clk;
2796 ++hits;
2799 clk = GetPskClock("", false);
2800 if (clk > 0) {
2801 // allow undo
2802 save_restoreGB(GRAPH_SAVE);
2803 // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise)
2804 CmdLtrim("-i 160");
2805 if ((PSKDemod(0, 0, 6, false) == PM3_SUCCESS)) {
2806 tests[hits].modulation = DEMOD_PSK1;
2807 tests[hits].bitrate = clk;
2808 ++hits;
2810 // get psk carrier
2811 tests[hits].carrier = GetPskCarrier(false);
2813 //undo trim samples
2814 save_restoreGB(GRAPH_RESTORE);
2818 if (hits) {
2819 PrintAndLogEx(SUCCESS, "Found [%d] possible matches for modulation.", hits);
2820 for (int i = 0; i < hits; ++i) {
2821 PrintAndLogEx(INFO, "--[%d]---------------", i + 1);
2822 print_modulation(tests[i]);
2824 return PM3_SUCCESS;
2825 } else {
2826 PrintAndLogEx(INFO, "Signal doesn't match");
2827 return PM3_ESOFT;
2831 static int CmdDataModulationSearch(const char *Cmd) {
2832 CLIParserContext *ctx;
2833 CLIParserInit(&ctx, "data modulation",
2834 "search LF signal after clock and modulation\n",
2835 "data modulation"
2838 void *argtable[] = {
2839 arg_param_begin,
2840 arg_param_end
2842 CLIExecWithReturn(ctx, Cmd, argtable, true);
2843 CLIParserFree(ctx);
2844 return try_detect_modulation();
2847 static int CmdAsn1Decoder(const char *Cmd) {
2849 CLIParserContext *ctx;
2850 CLIParserInit(&ctx, "data asn1",
2851 "Decode ASN1 bytearray\n"
2853 "data asn1 -d 303381050186922305a5020500a6088101010403030008a7188516eeee4facacf4fbde5e5c49d95e55bfbca74267b02407a9020500\n"
2856 void *argtable[] = {
2857 arg_param_begin,
2858 arg_str1("d", NULL, "<hex>", "ASN1 encoded byte array"),
2859 arg_param_end
2861 CLIExecWithReturn(ctx, Cmd, argtable, false);
2862 int dlen = 256;
2863 uint8_t data[256];
2864 CLIGetHexWithReturn(ctx, 1, data, &dlen);
2865 CLIParserFree(ctx);
2867 // print ASN1 decoded array in TLV view
2868 PrintAndLogEx(INFO, "---------------- " _CYAN_("ASN1 TLV") " -----------------");
2869 asn1_print(data, dlen, " ");
2870 PrintAndLogEx(NORMAL, "");
2871 return PM3_SUCCESS;
2874 static command_t CommandTable[] = {
2875 {"help", CmdHelp, AlwaysAvailable, "This help"},
2877 {"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("Modulation") "-------------------------"},
2878 {"biphaserawdecode", CmdBiphaseDecodeRaw, AlwaysAvailable, "Biphase decode bin stream in DemodBuffer"},
2879 {"detectclock", CmdDetectClockRate, AlwaysAvailable, "Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer"},
2880 {"fsktonrz", CmdFSKToNRZ, AlwaysAvailable, "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)"},
2881 {"manrawdecode", Cmdmandecoderaw, AlwaysAvailable, "Manchester decode binary stream in DemodBuffer"},
2882 {"modulation", CmdDataModulationSearch, AlwaysAvailable, "Identify LF signal for clock and modulation"},
2883 {"rawdemod", CmdRawDemod, AlwaysAvailable, "Demodulate the data in the GraphBuffer and output binary"},
2885 {"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("Graph") "-------------------------"},
2886 {"askedgedetect", CmdAskEdgeDetect, AlwaysAvailable, "Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave"},
2887 {"autocorr", CmdAutoCorr, AlwaysAvailable, "Autocorrelation over window"},
2888 {"dirthreshold", CmdDirectionalThreshold, AlwaysAvailable, "Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev."},
2889 {"decimate", CmdDecimate, AlwaysAvailable, "Decimate samples"},
2890 {"undecimate", CmdUndecimate, AlwaysAvailable, "Un-decimate samples"},
2891 {"hide", CmdHide, AlwaysAvailable, "Hide graph window"},
2892 {"hpf", CmdHpf, AlwaysAvailable, "Remove DC offset from trace"},
2893 {"iir", CmdDataIIR, AlwaysAvailable, "Apply IIR buttersworth filter on plot data"},
2894 {"grid", CmdGrid, AlwaysAvailable, "overlay grid on graph window"},
2895 {"ltrim", CmdLtrim, AlwaysAvailable, "Trim samples from left of trace"},
2896 {"mtrim", CmdMtrim, AlwaysAvailable, "Trim out samples from the specified start to the specified stop"},
2897 {"norm", CmdNorm, AlwaysAvailable, "Normalize max/min to +/-128"},
2898 {"plot", CmdPlot, AlwaysAvailable, "Show graph window"},
2899 {"rtrim", CmdRtrim, AlwaysAvailable, "Trim samples from right of trace"},
2900 {"setgraphmarkers", CmdSetGraphMarkers, AlwaysAvailable, "Set blue and orange marker in graph window"},
2901 {"shiftgraphzero", CmdGraphShiftZero, AlwaysAvailable, "Shift 0 for Graphed wave + or - shift value"},
2902 {"timescale", CmdTimeScale, AlwaysAvailable, "Set a timescale to get a differential reading between the yellow and purple markers as time duration"},
2903 {"zerocrossings", CmdZerocrossings, AlwaysAvailable, "Count time between zero-crossings"},
2904 {"convertbitstream", CmdConvertBitStream, AlwaysAvailable, "Convert GraphBuffer's 0/1 values to 127 / -127"},
2905 {"getbitstream", CmdGetBitStream, AlwaysAvailable, "Convert GraphBuffer's >=1 values to 1 and <1 to 0"},
2907 {"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("General") "-------------------------"},
2908 {"asn1", CmdAsn1Decoder, AlwaysAvailable, "asn1 decoder"},
2909 {"bin2hex", Cmdbin2hex, AlwaysAvailable, "Converts binary to hexadecimal"},
2910 {"bitsamples", CmdBitsamples, IfPm3Present, "Get raw samples as bitstring"},
2911 {"clear", CmdBuffClear, AlwaysAvailable, "Clears bigbuf on deviceside and graph window"},
2912 {"hexsamples", CmdHexsamples, IfPm3Present, "Dump big buffer as hex bytes"},
2913 {"hex2bin", Cmdhex2bin, AlwaysAvailable, "Converts hexadecimal to binary"},
2914 {"load", CmdLoad, AlwaysAvailable, "Load contents of file into graph window"},
2915 {"print", CmdPrintDemodBuff, AlwaysAvailable, "Print the data in the DemodBuffer"},
2916 {"samples", CmdSamples, IfPm3Present, "Get raw samples for graph window (GraphBuffer)"},
2917 {"save", CmdSave, AlwaysAvailable, "Save signal trace data (from graph window)"},
2918 {"setdebugmode", CmdSetDebugMode, AlwaysAvailable, "Set Debugging Level on client side"},
2919 {"tune", CmdTuneSamples, IfPm3Present, "Measure tuning of device antenna. Results shown in graph window"},
2920 {NULL, NULL, NULL, NULL}
2923 static int CmdHelp(const char *Cmd) {
2924 (void)Cmd; // Cmd is not used so far
2925 CmdsHelp(CommandTable);
2926 return PM3_SUCCESS;
2929 int CmdData(const char *Cmd) {
2930 clearCommandBuffer();
2931 return CmdsParse(CommandTable, Cmd);