1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
3 // modified Marshmellow,
4 // modified Iceman 2019, 2020, 2021
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
9 //-----------------------------------------------------------------------------
10 // Data and Graph commands
11 //-----------------------------------------------------------------------------
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
22 #include "graph.h" // for graph data
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;
37 static int CmdHelp(const char *Cmd
);
39 //set the demod buffer with given array ofq binary (one bit per byte)
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
);
67 static double rms(double *v, size_t n) {
69 for (size_t i = 0; i < n; i++)
74 static int cmp_int(const void *a, const void *b) {
75 if (*(const int *)a < * (const int *)b)
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)
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
) {
100 for (size_t i
= 0; i
< n
; i
++)
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);
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
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;
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
;
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",
174 arg_lit0("0", NULL
, "no debug messages"),
175 arg_lit0("1", NULL
, "debug"),
176 arg_lit0("2", NULL
, "verbose debugging"),
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);
186 if (dg_0
+ dg_1
+ dg_2
> 1) {
187 PrintAndLogEx(INFO
, "Select only one option");
199 switch (g_debugMode
) {
201 PrintAndLogEx(INFO
, "client debug level... %u ( no debug messages )", g_debugMode
);
204 PrintAndLogEx(INFO
, "client debug level... %u ( debug messages )", g_debugMode
);
207 PrintAndLogEx(INFO
, "client debug level... %u ( verbose debug messages )", g_debugMode
);
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
;
220 PrintAndLogEx(WARNING
, "Demodbuffer is empty");
224 uint8_t *buf
= calloc(len
, sizeof(uint8_t));
226 PrintAndLogEx(WARNING
, "dail, cannot allocate memory");
229 memcpy(buf
, DemodBuffer
, len
);
236 if (len
> (DemodBufferLen
- offset
))
237 len
= (DemodBufferLen
- offset
);
240 for (i
= 0; i
< len
; i
++) {
241 if (p
[i
] == 1) break;
246 if (len
> (DemodBufferLen
- offset
)) {
247 len
= (DemodBufferLen
- offset
);
256 for (size_t i
= 0; i
< len
; i
++) {
268 char hex
[512] = {0x00};
269 int num_bits
= binarraytohex(hex
, sizeof(hex
), (char *)p
, len
);
275 PrintAndLogEx(SUCCESS
, "DemodBuffer:\n%s", hex
);
277 PrintAndLogEx(SUCCESS
, "DemodBuffer:\n%s", sprint_bin_break(buf
+ offset
, len
, 32));
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",
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)"),
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);
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",
327 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
331 for (uint32_t i
= 0; i
< GraphTraceLen
; i
++) {
332 GraphBuffer
[i
] = (GraphBuffer
[i
] >= 1) ? 1 : 0;
334 RepaintGraphWindow();
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"
349 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
352 if (isGraphBitstream()) {
353 convertGraphFromBitstream();
356 convertGraphFromBitstreamEx(-126, -127);
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 "
380 maxlen
= pm3_capabilities
.bigbuf_size
;
382 uint8_t *bits
= calloc(MAX_GRAPH_TRACE_LEN
, sizeof(uint8_t));
384 PrintAndLogEx(INFO
, "failed to allocate memory");
388 size_t bitlen
= getFromGraphBuf(bits
);
390 PrintAndLogEx(DEBUG
, "DEBUG: (ASKDemod_ext) #samples from graphbuff: %zu", bitlen
);
397 if (maxlen
< bitlen
&& maxlen
!= 0)
402 //amplify signal before ST check
404 askAmp(bits
, bitlen
);
407 size_t ststart
= 0, stend
= 0;
409 bool st
= DetectST(bits
, &bitlen
, &foundclk
, &ststart
, &stend
);
412 if (foundclk
== 32 || foundclk
== 64) {
419 CursorCPos
= ststart
;
422 PrintAndLogEx(DEBUG
, "Found Sequence Terminator - First one is shown by orange / blue graph markers");
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"
431 , (invert
) ? "inverted," : ""
439 if (errCnt
> maxErr
) {
440 PrintAndLogEx(DEBUG
, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%zu, clock:%d"
450 PrintAndLogEx(DEBUG
, "DEBUG: (ASKDemod_ext) using clock:%d, %sbits found:%zu, start index %d"
452 , (invert
) ? "inverted, " : ""
459 setDemodBuff(bits
, bitlen
, 0);
460 setClockGrid(clk
, start_idx
);
464 PrintAndLogEx(DEBUG
, "# Errors during demoding (shown as 7 in bit stream): %d", errCnt
);
467 PrintAndLogEx(SUCCESS
, _YELLOW_("ASK/Manchester") " - clock %d - decoded bitstream", clk
);
468 PrintAndLogEx(INFO
, "---------------------------------------------");
470 PrintAndLogEx(SUCCESS
, _YELLOW_("ASK/Raw") " - clock %d - decoded bitstream", clk
);
471 PrintAndLogEx(INFO
, "--------------------------------------");
474 printDemodBuff(0, false, false, false);
479 AskEm410xDecode(true, &hi
, &lo
);
485 int ASKDemod(int clk
, int invert
, int maxErr
, size_t maxlen
, bool amplify
, bool verbose
, bool emSearch
, uint8_t askType
) {
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"
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]"),
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;
523 return ASKDemod_ext(clk
, invert
, max_err
, max_len
, amplify
, true, true, 1, &st
);
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)",
538 arg_lit0("i", "inv", "invert output"),
539 arg_int0(NULL
, "err", "<dec>", "set max errors tolerated (def 20)"),
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);
547 if (DemodBufferLen
== 0) {
548 PrintAndLogEx(WARNING
, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`"));
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;
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");
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
);
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));
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);
602 * @author marshmellow
604 * decodes 01 or 10 -> ZERO
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"
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)"),
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);
633 if (DemodBufferLen
== 0) {
634 PrintAndLogEx(WARNING
, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`"));
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
);
644 PrintAndLogEx(ERR
, "Error during decode " _RED_("%i"), err_cnt
);
647 if (err_cnt
> max_err
) {
648 PrintAndLogEx(ERR
, "Too many errors attempting to decode " _RED_("%i"), err_cnt
);
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
661 setDemodBuff(DemodBuffer
, DemodBufferLen
- offset
, offset
);
664 setClockGrid(g_DemodClock
, g_DemodStartIdx
+ g_DemodClock
* offset
/ 2);
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
);
675 PrintAndLogEx(DEBUG
, "DEBUG: no data in graphbuf");
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
);
686 //attempt to Biphase decode BitStream
687 errCnt
= BiphaseRawDecode(bs
, &size
, &offset
, invert
);
689 if (g_debugMode
|| verbose
) PrintAndLogEx(DEBUG
, "DEBUG: Error BiphaseRawDecode: %d", errCnt
);
692 if (errCnt
> maxErr
) {
693 if (g_debugMode
|| verbose
) PrintAndLogEx(DEBUG
, "DEBUG: Error BiphaseRawDecode too many errors: %d", errCnt
);
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);
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"
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)"),
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;
735 return ASKbiphaseDemod(offset
, clk
, invert
, max_err
, true);
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"
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]"),
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;
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
) {
774 if (window
> len
) window
= len
;
776 if (verbose
) PrintAndLogEx(INFO
, "performing " _YELLOW_("%zu") " correlations", GraphTraceLen
- window
);
779 double autocv
= 0.0; // Autocovariance value
780 size_t correlation
= 0;
784 double mean
= compute_mean(in
, len
);
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.
805 correlation
= i
- lastmax
;
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
) {
820 for (size_t i
= idx
+ 1; i
<= window
; ++i
) {
821 if (correl_buf
[i
] > hi_1
) {
822 hi_1
= correl_buf
[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
);
836 PrintAndLogEx(FAILED
, "no repeating pattern found, try increasing window size");
839 int retval
= correlation
;
841 //GraphTraceLen = GraphTraceLen - window;
842 memcpy(out
, correl_buf
, len
* sizeof(int));
844 setClockGrid(distance
, idx
);
847 setClockGrid(correlation
, idx
);
850 CursorDPos
= idx_1
+ retval
;
852 RepaintGraphWindow();
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"
868 arg_lit0("g", NULL
, "save back to GraphBuffer (overwrite)"),
869 arg_u64_0("w", "win", "<dec>", "window length for correlation. def 4000"),
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);
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");
885 if (window
>= GraphTraceLen
) {
886 PrintAndLogEx(WARNING
, "window must be smaller than trace (" _YELLOW_("%zu") " samples)", GraphTraceLen
);
890 AutoCorrelate(GraphBuffer
, GraphBuffer
, GraphTraceLen
, window
, updateGrph
, true);
894 static int CmdBitsamples(const char *Cmd
) {
896 CLIParserContext
*ctx
;
897 CLIParserInit(&ctx
, "data bitsamples",
898 "Get raw samples from device as bitstring",
905 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
911 if (!GetFromDevice(BIG_BUF
, got
, sizeof(got
), 0, NULL
, 0, NULL
, 2500, false)) {
912 PrintAndLogEx(WARNING
, "command execution time out");
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;
921 GraphBuffer
[cnt
++] = 0;
925 RepaintGraphWindow();
929 static int CmdBuffClear(const char *Cmd
) {
930 CLIParserContext
*ctx
;
931 CLIParserInit(&ctx
, "data clear",
932 "This function clears the bigbuff on deviceside\n"
940 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
942 clearCommandBuffer();
943 SendCommandNG(CMD_BUFF_CLEAR
, NULL
, 0);
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",
959 arg_int0("n", NULL
, "<dec>", "factor to reduce sample set (default 2)"),
962 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
963 int n
= arg_get_int_def(ctx
, 1, 2);
966 for (size_t i
= 0; i
< (GraphTraceLen
/ n
); ++i
)
967 GraphBuffer
[i
] = GraphBuffer
[i
* n
];
970 PrintAndLogEx(SUCCESS
, "decimated by " _GREEN_("%u"), n
);
971 RepaintGraphWindow();
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
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",
986 "data undecimate -n 4\n"
991 arg_int0("n", NULL
, "<dec>", "factor to repeat each sample (default 2)"),
994 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
995 int factor
= arg_get_int_def(ctx
, 1, 2);
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
) {
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]
1013 memcpy(GraphBuffer
, swap
, s_index
* sizeof(int));
1014 GraphTraceLen
= s_index
;
1015 RepaintGraphWindow();
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
[] = {
1030 arg_int1("n", NULL
, "<dec>", "shift + or -"),
1033 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
1034 int shift
= arg_get_int_def(ctx
, 1, 0);
1037 for (size_t i
= 0; i
< GraphTraceLen
; i
++) {
1038 int shiftedVal
= GraphBuffer
[i
] + shift
;
1040 if (shiftedVal
> 127)
1042 else if (shiftedVal
< -127)
1044 GraphBuffer
[i
] = shiftedVal
;
1050 int AskEdgeDetect(const int *in
, int *out
, int len
, int threshold
) {
1052 for (int i
= 1; i
< len
; i
++) {
1053 if (in
[i
] - in
[i
- 1] >= threshold
) //large jump up
1055 else if (in
[i
] - in
[i
- 1] <= -1 * threshold
) //large jump down
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
[] = {
1075 arg_int0("t", "thres", "<dec>", "threshold, use 20 - 45 (def 25)"),
1078 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1079 int threshold
= arg_get_int_def(ctx
, 1, 25);
1082 PrintAndLogEx(INFO
, "using threshold " _YELLOW_("%i"), threshold
);
1083 int res
= AskEdgeDetect(GraphBuffer
, GraphBuffer
, GraphTraceLen
, threshold
);
1084 RepaintGraphWindow();
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
[] = {
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"),
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);
1115 int tmp
= (a
+ f
+ n
+ p
);
1117 PrintAndLogEx(WARNING
, "Only specify one modulation");
1122 GetAskClock("", true);
1125 GetFskClock("", true);
1128 GetNrzClock("", true);
1131 GetPskClock("", true);
1133 RepaintGraphWindow();
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) {
1145 memcpy(fskType
, "FSK2a", 5);
1147 memcpy(fskType
, "FSK2", 4);
1149 } else if (fchigh
== 8 && fclow
== 5) {
1152 memcpy(fskType
, "FSK1", 4);
1154 memcpy(fskType
, "FSK1a", 5);
1157 memcpy(fskType
, "FSK??", 5);
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
) {
1169 PrintAndLogEx(INFO
, "signal looks like noise");
1174 uint8_t *bits
= calloc(MAX_GRAPH_TRACE_LEN
, sizeof(uint8_t));
1176 PrintAndLogEx(FAILED
, "failed to allocate memory");
1180 size_t bitlen
= getFromGraphBuf(bits
);
1182 PrintAndLogEx(DEBUG
, "DEBUG: no data in graphbuf");
1187 //get field clock lengths
1188 if (!fchigh
|| !fclow
) {
1189 uint16_t fcs
= countFC(bits
, bitlen
, true);
1194 fchigh
= (fcs
>> 8) & 0x00FF;
1195 fclow
= fcs
& 0x00FF;
1198 //get bit clock length
1200 int firstClockEdge
= 0; //todo - align grid on graph with this...
1201 rfLen
= detectFSKClk(bits
, bitlen
, fchigh
, fclow
, &firstClockEdge
);
1202 if (!rfLen
) rfLen
= 50;
1206 int size
= fskdemod(bits
, bitlen
, rfLen
, invert
, fchigh
, fclow
, &start_idx
);
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"
1215 , (invert
) ? "inverted, " : ""
1219 PrintAndLogEx(NORMAL
, "");
1220 PrintAndLogEx(SUCCESS
, _YELLOW_("%s") " decoded bitstream", GetFSKType(fchigh
, fclow
, invert
));
1221 PrintAndLogEx(INFO
, "-----------------------");
1222 printDemodBuff(0, false, invert
, false);
1226 PrintAndLogEx(DEBUG
, "no FSK data found");
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
[] = {
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)"),
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;
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
) {
1272 PrintAndLogEx(INFO
, "signal looks like noise");
1277 uint8_t *bits
= calloc(MAX_GRAPH_TRACE_LEN
, sizeof(uint8_t));
1279 PrintAndLogEx(FAILED
, "failed to allocate memory");
1282 size_t bitlen
= getFromGraphBuf(bits
);
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
);
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
);
1300 if (verbose
|| g_debugMode
) {
1301 PrintAndLogEx(DEBUG
, "DEBUG: (PSKdemod) Using Clock:%d, invert:%d, Bits Found:%zu", clk
, invert
, bitlen
);
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
);
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
) {
1322 PrintAndLogEx(INFO
, "signal looks like noise");
1327 uint8_t *bits
= calloc(MAX_GRAPH_TRACE_LEN
, sizeof(uint8_t));
1329 PrintAndLogEx(FAILED
, "failed to allocate memory");
1333 size_t bitlen
= getFromGraphBuf(bits
);
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
);
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
);
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);
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
[] = {
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)"),
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;
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
[] = {
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)"),
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;
1423 int ans
= PSKDemod(clk
, invert
, max_err
, true);
1425 if (ans
!= PM3_SUCCESS
) {
1426 if (g_debugMode
) PrintAndLogEx(ERR
, "Error demoding: %d", ans
);
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);
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
[] = {
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)"),
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;
1461 int ans
= PSKDemod(clk
, invert
, max_err
, true);
1462 if (ans
!= PM3_SUCCESS
) {
1463 if (g_debugMode
) PrintAndLogEx(ERR
, "Error demoding: %d", ans
);
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);
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
[] = {
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"),
1502 size_t n
= MIN(strlen(Cmd
), 4);
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);
1517 int foo
= (ab
+ am
+ ar
+ fs
+ nr
+ p1
+ p2
);
1519 PrintAndLogEx(WARNING
, "please, select only one modulation");
1523 PrintAndLogEx(WARNING
, "please, select a modulation");
1528 const char *s
= Cmd
+ n
;
1530 ans
= CmdFSKrawdemod(s
);
1532 ans
= Cmdaskbiphdemod(s
);
1534 ans
= Cmdaskmandemod(s
);
1536 ans
= Cmdaskrawdemod(s
);
1538 ans
= CmdNRZrawDemod(s
);
1540 ans
= CmdPSK1rawDemod(s
);
1542 ans
= CmdPSK2rawDemod(s
);
1547 void setClockGrid(uint32_t clk
, int offset
) {
1548 g_DemodStartIdx
= offset
;
1550 if (clk
== 0 && offset
== 0)
1551 PrintAndLogEx(DEBUG
, "DEBUG: (setClockGrid) clear settings");
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
) {
1563 PlotGridXdefault
= 0;
1564 RepaintGraphWindow();
1567 GridOffset
= offset
;
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
[] = {
1584 arg_dbl0("x", NULL
, "<dec>", "plot grid X coord"),
1585 arg_dbl0("y", NULL
, "<dec>", "plot grid Y coord"),
1588 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1589 PlotGridX
= arg_get_dbl_def(ctx
, 1, 0);
1590 PlotGridY
= arg_get_dbl_def(ctx
, 2, 0);
1592 PrintAndLogEx(INFO
, "Setting X %.0f Y %.0f", PlotGridX
, PlotGridY
);
1593 PlotGridXdefault
= PlotGridX
;
1594 PlotGridYdefault
= PlotGridY
;
1595 RepaintGraphWindow();
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
[] = {
1608 arg_u64_0("a", NULL
, "<dec>", "orange marker"),
1609 arg_u64_0("b", NULL
, "<dec>", "blue marker"),
1612 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1613 CursorCPos
= arg_get_u32_def(ctx
, 1, 0);
1614 CursorDPos
= arg_get_u32_def(ctx
, 2, 0);
1616 PrintAndLogEx(INFO
, "Setting orange %u blue %u", CursorCPos
, CursorDPos
);
1617 RepaintGraphWindow();
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
[] = {
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"),
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);
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"
1652 , pm3_capabilities
.bigbuf_size
1657 if (!GetFromDevice(BIG_BUF
, got
, requested
, offset
, NULL
, 0, NULL
, 2500, false)) {
1658 PrintAndLogEx(WARNING
, "command execution time out");
1662 print_hex_break(got
, requested
, breaks
);
1666 static int CmdHide(const char *Cmd
) {
1667 CLIParserContext
*ctx
;
1668 CLIParserInit(&ctx
, "data hide",
1669 "Show graph window",
1672 void *argtable
[] = {
1676 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
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",
1689 void *argtable
[] = {
1693 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
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();
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
) {
1716 for (int i
= 0 ; i
< bits_per_sample
; i
++)
1717 val
|= (_headBit(b
) << (7 - i
));
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
) {
1729 PrintAndLogEx(WARNING
, "error, end (%u) is smaller than start (%u)", end
, start
);
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;
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};
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;
1777 if (verbose
) PrintAndLogEx(INFO
, "Unpacked %d samples", j
);
1780 for (uint32_t j
= 0; j
< n
; j
++) {
1781 GraphBuffer
[j
] = ((int)got
[j
]) - 127;
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
);
1793 RepaintGraphWindow();
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.",
1804 "data samples -n 10000"
1806 void *argtable
[] = {
1808 arg_int0("n", NULL
, "<dec>", "num of samples (512 - 40000)"),
1809 arg_lit0("v", "verbose", "verbose"),
1812 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1813 int n
= arg_get_int_def(ctx
, 1, 0);
1814 bool verbose
= arg_get_lit(ctx
, 2);
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",
1828 void *argtable
[] = {
1832 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
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
1845 RepaintGraphWindow();
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)) {
1860 if (timeout
>= timeout_max
) {
1861 PrintAndLogEx(WARNING
, "\nNo response from Proxmark3. Aborting...");
1862 return PM3_ETIMEOUT
;
1865 PrintAndLogEx(INPLACE
, "% 3i", timeout_max
- timeout
);
1868 if (resp
.status
!= PM3_SUCCESS
) {
1869 PrintAndLogEx(WARNING
, "Antenna tuning failed");
1873 PrintAndLogEx(NORMAL
, "");
1874 PrintAndLogEx(INFO
, "---------- " _CYAN_("LF Antenna") " ----------");
1884 uint8_t results
[256];
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
)) {
1915 if ((s2
!= 0) && (package
->results
[i
] < v_3db_scaled
)) {
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
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");
1956 memset(judgement
, 0, sizeof(judgement
));
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"));
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") " ----------");
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"));
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.
1992 for (int i
= 0; i
< 256; i
++) {
1993 GraphBuffer
[i
] = package
->results
[i
] - 128;
1994 test1
+= package
->results
[i
];
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
;
2004 RepaintGraphWindow();
2007 PrintAndLogEx(FAILED
, "\nNot showing LF tuning graph since all values is zero.\n\n");
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
[] = {
2023 arg_str1("f", "file", "<fn>", "file to load"),
2026 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2029 char filename
[FILE_PATH_SIZE
] = {0};
2030 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
2034 if (searchFile(&path
, TRACES_SUBDIR
, filename
, ".pm3", true) != PM3_SUCCESS
) {
2035 if (searchFile(&path
, TRACES_SUBDIR
, filename
, "", false) != PM3_SUCCESS
) {
2040 FILE *f
= fopen(path
, "r");
2042 PrintAndLogEx(WARNING
, "couldn't open '%s'", path
);
2050 while (fgets(line
, sizeof(line
), f
)) {
2051 GraphBuffer
[GraphTraceLen
] = atoi(line
);
2054 if (GraphTraceLen
>= MAX_GRAPH_TRACE_LEN
)
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
);
2070 RepaintGraphWindow();
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
[] = {
2083 arg_u64_1("i", "idx", "<dec>", "from index to beginning trace"),
2086 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2087 uint32_t ds
= arg_get_u32(ctx
, 1);
2091 if (GraphTraceLen
<= ds
) {
2092 PrintAndLogEx(WARNING
, "index out of bounds");
2096 for (uint32_t i
= ds
; i
< GraphTraceLen
; ++i
)
2097 GraphBuffer
[i
- ds
] = GraphBuffer
[i
];
2099 GraphTraceLen
-= ds
;
2100 g_DemodStartIdx
-= ds
;
2101 RepaintGraphWindow();
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
[] = {
2115 arg_u64_1("i", "idx", "<dec>", "from index to end trace"),
2118 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2119 uint32_t ds
= arg_get_u32(ctx
, 1);
2123 if (GraphTraceLen
<= ds
) {
2124 PrintAndLogEx(WARNING
, "index out of bounds");
2129 RepaintGraphWindow();
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
[] = {
2143 arg_u64_1("s", "start", "<dec>", "start point"),
2144 arg_u64_1("e", "end", "<dec>", "end point"),
2147 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2148 uint32_t start
= arg_get_u32(ctx
, 1);
2149 uint32_t stop
= arg_get_u32(ctx
, 2);
2152 if (start
> GraphTraceLen
|| stop
> GraphTraceLen
|| start
>= stop
) {
2153 PrintAndLogEx(WARNING
, "start and end points doesn't align");
2157 // leave start position sample
2160 GraphTraceLen
= stop
- start
;
2161 for (uint32_t i
= 0; i
< GraphTraceLen
; i
++) {
2162 GraphBuffer
[i
] = GraphBuffer
[start
+ i
];
2168 int CmdNorm(const char *Cmd
) {
2170 CLIParserContext
*ctx
;
2171 CLIParserInit(&ctx
, "data norm",
2172 "Normalize max/min to +/-128",
2175 void *argtable
[] = {
2179 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
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
];
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();
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",
2213 void *argtable
[] = {
2217 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
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
[] = {
2236 arg_lit0("w", "wave", "save as wave format (.wav)"),
2237 arg_str1("f", "file", "<fn w/o ext>", "save file name"),
2240 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2242 bool as_wave
= arg_get_lit(ctx
, 1);
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
);
2252 return saveFileWAVE(filename
, GraphBuffer
, GraphTraceLen
);
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
[] = {
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)"),
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;
2281 CursorScaleFactorUnit
[0] = '\x00';
2282 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)CursorScaleFactorUnit
, sizeof(CursorScaleFactorUnit
), &len
);
2284 RepaintGraphWindow();
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.
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.
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.
2307 lastValue
= out
[i
]; // Buffer last value as we overwrite it.
2308 out
[i
] = out
[i
- 1];
2312 // Align with first edited sample.
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
[] = {
2325 arg_int1("d", "down", "<dec>", "threshold down"),
2326 arg_int1("u", "up", "<dec>", "threshold up"),
2329 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2330 int8_t down
= arg_get_int(ctx
, 1);
2331 int8_t up
= arg_get_int(ctx
, 2);
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();
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
[] = {
2358 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
2361 // Zero-crossings aren't meaningful unless the signal is zero-mean.
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.
2370 GraphBuffer
[i
] = lastZc
;
2372 // Change in sign, reset the sample count.
2374 GraphBuffer
[i
] = lastZc
;
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();
2391 * @brief Utility for conversion via cmdline.
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
[] = {
2405 arg_strx0("d", "data", "<bin>", "binary string to convert"),
2408 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2410 uint8_t binarr
[400] = {0x00};
2411 int res
= CLIParamBinToBuf(arg_get_str(ctx
, 1), binarr
, sizeof(binarr
), &blen
);
2415 PrintAndLogEx(FAILED
, "Error parsing binary string");
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
];
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
));
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
[] = {
2452 arg_str0("d", "data", "<hex>", "bytes to convert"),
2455 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2457 char data
[200] = {0x00};
2458 int res
= CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)data
, sizeof(data
), &dlen
);
2462 PrintAndLogEx(FAILED
, "Error parsing bytes");
2466 for (int i
= 0; i
< dlen
; i
++) {
2468 if (isxdigit(x
) == false) {
2469 PrintAndLogEx(ERR
, "Non hex digit found");
2474 PrintAndLogEx(SUCCESS
, "" NOLF
);
2475 for (int i
= 0; i
< dlen
; i
++) {
2479 if (x
>= 'a' && x
<= 'f')
2481 // convert to numeric value
2482 if (x
>= '0' && x
<= '9')
2484 else if (x
>= 'A' && x
<= 'F')
2489 for (int j
= 0 ; j
< 4 ; ++j
) {
2490 PrintAndLogEx(NORMAL
, "%d" NOLF
, (x
>> (3 - j
)) & 1);
2493 PrintAndLogEx(NORMAL
, "");
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
) {
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
++) {
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;
2540 // add last -1 modifiers
2541 for (k
= 0; k
< Right_Modifier
; k
++) {
2542 LowTone
[((i
- 1) * LowToneFC
) + Left_Modifier
+ j
+ k
] = -1;
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
++) {
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
) {
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");
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
);
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",
2644 "data fsktonrz -c 32 --low 8 --hi 10");
2646 void *argtable
[] = {
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"),
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);
2662 int ans
= FSKToNRZ(GraphBuffer
, &GraphTraceLen
, clk
, fc_low
, fc_high
);
2664 RepaintGraphWindow();
2668 static int CmdDataIIR(const char *Cmd
) {
2670 CLIParserContext
*ctx
;
2671 CLIParserInit(&ctx
, "data iir",
2672 "Apply IIR buttersworth filter on plot data",
2675 void *argtable
[] = {
2677 arg_u64_1("n", NULL
, "<dec>", "factor n"),
2680 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2681 uint8_t k
= (arg_get_u32_def(ctx
, 1, 0) & 0xFF);
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();
2695 t55xx_modulation modulation
;
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
) {
2710 PrintAndLogEx(SUCCESS
, " Carrier rate...... %d", b
.carrier
);
2717 PrintAndLogEx(SUCCESS
, " Field Clocks...... FC/%u, FC/%u", b
.fc1
, b
.fc2
);
2726 PrintAndLogEx(NORMAL
, "");
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;
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
;
2757 clk
= GetAskClock("", false);
2763 // false = no amplify
2764 // false = no verbose
2765 // false = no emSearch
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
;
2773 // "0 0 1 " == clock auto, invert true, maxError 1.
2774 // false = no verbose
2775 // false = no emSearch
2780 if ((ASKbiphaseDemod(0, 0, 0, 2, false) == PM3_SUCCESS
)) {
2781 tests
[hits
].modulation
= DEMOD_BI
;
2782 tests
[hits
].bitrate
= clk
;
2786 if ((ASKbiphaseDemod(0, 0, 1, 2, false) == PM3_SUCCESS
)) {
2787 tests
[hits
].modulation
= DEMOD_BIa
;
2788 tests
[hits
].bitrate
= clk
;
2792 clk
= GetNrzClock("", false);
2793 if ((NRZrawDemod(0, 0, 1, false) == PM3_SUCCESS
)) {
2794 tests
[hits
].modulation
= DEMOD_NRZ
;
2795 tests
[hits
].bitrate
= clk
;
2799 clk
= GetPskClock("", false);
2802 save_restoreGB(GRAPH_SAVE
);
2803 // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise)
2805 if ((PSKDemod(0, 0, 6, false) == PM3_SUCCESS
)) {
2806 tests
[hits
].modulation
= DEMOD_PSK1
;
2807 tests
[hits
].bitrate
= clk
;
2811 tests
[hits
].carrier
= GetPskCarrier(false);
2814 save_restoreGB(GRAPH_RESTORE
);
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
]);
2826 PrintAndLogEx(INFO
, "Signal doesn't match");
2831 static int CmdDataModulationSearch(const char *Cmd
) {
2832 CLIParserContext
*ctx
;
2833 CLIParserInit(&ctx
, "data modulation",
2834 "search LF signal after clock and modulation\n",
2838 void *argtable
[] = {
2842 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
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
[] = {
2858 arg_str1("d", NULL
, "<hex>", "ASN1 encoded byte array"),
2861 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2864 CLIGetHexWithReturn(ctx
, 1, data
, &dlen
);
2867 // print ASN1 decoded array in TLV view
2868 PrintAndLogEx(INFO
, "---------------- " _CYAN_("ASN1 TLV") " -----------------");
2869 asn1_print(data
, dlen
, " ");
2870 PrintAndLogEx(NORMAL
, "");
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
);
2929 int CmdData(const char *Cmd
) {
2930 clearCommandBuffer();
2931 return CmdsParse(CommandTable
, Cmd
);