mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-10-03 21:18:45 +00:00
Three things that all cost memory or correctness once BigBuf is larger than 64KB, which it already is on PM5. BigBuf_max_traceLen() returned a uint16_t while s_bigbuf_hi is a uint32_t. At 33272 on AT91 that is harmless; on AT32, where BigBuf runs to several hundred kbyte, it truncates and LF sampling asks for a fraction of the buffer that is there. Widened, along with the four callers that assigned it straight back into a uint16_t. BigBuf_malloc() and BigBuf_calloc() took a uint16_t chunksize, so a request of exactly 65536 arrived as 0 and anything above wrapped. It returned NULL for the 65536 case by accident, not by design. Both take a uint32_t now and the guard tests the upper bound explicitly, so an oversized request fails like any other allocation that does not fit. The 14a tag simulation allocated its dynamic modulation buffer as a flat 512, or 4096 for ST25TA. prepare_tag_modulation() memcpy's the encoded answer out of the ToSend buffer and tosend_stuffbit() hard caps that at TOSEND_BUFFER_SIZE, so 1788 of ST25TA's 4096 could never be reached -- it was the largest single allocation of any tag simulation for nothing. Meanwhile 512 is 68 bytes short of what a full 64 byte response encodes to, at 9 bytes per byte plus 4, so those answers were refused at modulation time. Both are now derived from the response size and capped at what ToSend can hold: 580 for the default tag types, 2308 for ST25TA. That also fixes the reason the 4096 never helped -- all six call sites passed the 512 macro as max_buffer_size rather than the size actually allocated, so ST25TA allocated 4096 and then bounds checked against 512. Worst case for a simulation that also holds emulator memory drops from 12247 bytes of BigBuf to 10459. Built for client, RDV4 and PM3GENERIC. Not yet run on hardware. Co-Authored-By: Claude Opus 5 (1M context)
826 lines
24 KiB
C
826 lines
24 KiB
C
//-----------------------------------------------------------------------------
|
|
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// See LICENSE.txt for the text of the license.
|
|
//-----------------------------------------------------------------------------
|
|
// Miscellaneous routines for low frequency sampling.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "lfsampling.h"
|
|
|
|
#include "proxmark3_arm.h"
|
|
#include "BigBuf.h"
|
|
#include "fpga_loader.h"
|
|
#include "ticks_apis.h"
|
|
#include "fpga_apis.h"
|
|
#include "dbprint.h"
|
|
#include "util.h"
|
|
#include "lfdemod.h"
|
|
#include "string.h" // memset
|
|
#include "appmain.h" // print stack
|
|
#include "usb_cdc_apis.h"
|
|
#include "lfops.h"
|
|
|
|
/*
|
|
Default LF config is set to:
|
|
decimation = 1 (we keep 1 out of 1 samples)
|
|
bits_per_sample = 8
|
|
averaging = YES
|
|
divisor = 95 (125kHz)
|
|
trigger_threshold = 0
|
|
samples_to_skip = 0
|
|
verbose = YES
|
|
*/
|
|
|
|
static const sample_config def_config = {
|
|
.decimation = 1,
|
|
.bits_per_sample = 8,
|
|
.averaging = 1,
|
|
.divisor = LF_DIVISOR_125,
|
|
.trigger_threshold = 0,
|
|
.samples_to_skip = 0,
|
|
.verbose = false,
|
|
};
|
|
|
|
static sample_config config = { 1, 8, 1, LF_DIVISOR_125, 0, 0, true} ;
|
|
|
|
// Holds bit packed struct of samples.
|
|
static BitstreamOut_t data = {0, 0, 0};
|
|
|
|
// internal struct to keep track of samples gathered
|
|
static sampling_t samples = {0, 0, 0, 0};
|
|
|
|
void printLFConfig(void) {
|
|
uint32_t d = config.divisor;
|
|
DbpString(_CYAN_("LF Sampling config"));
|
|
Dbprintf(" [q] divisor............. %d ( "_GREEN_("%d.%02d kHz")" )", d, 12000 / (d + 1), ((1200000 + (d + 1) / 2) / (d + 1)) - ((12000 / (d + 1)) * 100));
|
|
Dbprintf(" [b] bits per sample..... %d", config.bits_per_sample);
|
|
Dbprintf(" [d] decimation.......... %d", config.decimation);
|
|
Dbprintf(" [a] averaging........... %s", (config.averaging) ? "yes" : "no");
|
|
Dbprintf(" [t] trigger threshold... %d", config.trigger_threshold);
|
|
Dbprintf(" [s] samples to skip..... %d ", config.samples_to_skip);
|
|
DbpString("");
|
|
}
|
|
|
|
void printSamples(void) {
|
|
DbpString(_CYAN_("LF Sampling memory usage"));
|
|
// Dbprintf(" decimation counter...%d", samples.dec_counter);
|
|
// Dbprintf(" sum..................%u", samples.sum);
|
|
Dbprintf(" counter.............. " _YELLOW_("%u"), samples.counter);
|
|
Dbprintf(" total saved.......... " _YELLOW_("%u"), samples.total_saved);
|
|
print_stack_usage();
|
|
}
|
|
|
|
|
|
void setDefaultSamplingConfig(void) {
|
|
setSamplingConfig(&def_config);
|
|
}
|
|
|
|
/**
|
|
* Called from the USB-handler to set the sampling configuration
|
|
* The sampling config is used for standard reading and sniffing.
|
|
*
|
|
* Other functions may read samples and ignore the sampling config,
|
|
* such as functions to read the UID from a prox tag or similar.
|
|
*
|
|
* Values set to '-1' implies no change
|
|
* @brief setSamplingConfig
|
|
* @param sc
|
|
*/
|
|
void setSamplingConfig(const sample_config *sc) {
|
|
|
|
// decimation (1-8) how many bits of adc sample value to save
|
|
if (sc->decimation > 0 && sc->decimation < 9)
|
|
config.decimation = sc->decimation;
|
|
|
|
// bits per sample (1-8)
|
|
if (sc->bits_per_sample > 0 && sc->bits_per_sample < 9)
|
|
config.bits_per_sample = sc->bits_per_sample;
|
|
|
|
//
|
|
if (sc->averaging > -1)
|
|
config.averaging = (sc->averaging > 0) ? 1 : 0;
|
|
|
|
// Frequency divisor (19 - 255)
|
|
if (sc->divisor > 18 && sc->divisor < 256)
|
|
config.divisor = sc->divisor;
|
|
|
|
// Start saving samples when adc value larger than trigger_threshold
|
|
if (sc->trigger_threshold > -1)
|
|
config.trigger_threshold = sc->trigger_threshold;
|
|
|
|
// Skip n adc samples before saving
|
|
if (sc->samples_to_skip > -1)
|
|
config.samples_to_skip = sc->samples_to_skip;
|
|
|
|
if (sc->verbose)
|
|
printLFConfig();
|
|
}
|
|
|
|
sample_config *getSamplingConfig(void) {
|
|
return &config;
|
|
}
|
|
|
|
void initSampleBuffer(uint32_t *sample_size) {
|
|
initSampleBufferEx(sample_size, false);
|
|
}
|
|
|
|
void initSampleBufferEx(uint32_t *sample_size, bool use_malloc) {
|
|
|
|
if (sample_size == NULL) {
|
|
return;
|
|
}
|
|
|
|
BigBuf_free_keep_EM();
|
|
|
|
// We can't erase the buffer now, it would drastically delay the acquisition
|
|
if (use_malloc) {
|
|
|
|
if (*sample_size == 0) {
|
|
*sample_size = BigBuf_max_traceLen();
|
|
data.buffer = BigBuf_get_addr();
|
|
} else {
|
|
*sample_size = MIN(*sample_size, BigBuf_max_traceLen());
|
|
data.buffer = BigBuf_calloc(*sample_size);
|
|
}
|
|
|
|
} else {
|
|
if (*sample_size == 0) {
|
|
*sample_size = BigBuf_max_traceLen();
|
|
} else {
|
|
*sample_size = MIN(*sample_size, BigBuf_max_traceLen());
|
|
}
|
|
data.buffer = BigBuf_get_addr();
|
|
}
|
|
|
|
// reset data stream
|
|
data.numbits = 0;
|
|
data.position = 0;
|
|
|
|
// reset samples
|
|
samples.dec_counter = 0;
|
|
samples.sum = 0;
|
|
samples.counter = *sample_size;
|
|
samples.total_saved = 0;
|
|
}
|
|
|
|
uint32_t getSampleCounter(void) {
|
|
return samples.total_saved;
|
|
}
|
|
|
|
void logSampleSimple(uint8_t sample) {
|
|
logSample(sample, config.decimation, config.bits_per_sample, config.averaging);
|
|
}
|
|
|
|
void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool avg) {
|
|
|
|
if (!data.buffer) {
|
|
return;
|
|
}
|
|
|
|
// keep track of total gather samples regardless how many was discarded.
|
|
if (samples.counter-- == 0) {
|
|
return;
|
|
}
|
|
|
|
if (bits_per_sample == 0) {
|
|
bits_per_sample = 1;
|
|
}
|
|
|
|
if (bits_per_sample > 8) {
|
|
bits_per_sample = 8;
|
|
}
|
|
|
|
if (decimation == 0) {
|
|
decimation = 1;
|
|
}
|
|
|
|
if (avg) {
|
|
samples.sum += sample;
|
|
}
|
|
|
|
// check decimation
|
|
if (decimation > 1) {
|
|
samples.dec_counter++;
|
|
|
|
if (samples.dec_counter < decimation) {
|
|
return;
|
|
}
|
|
|
|
samples.dec_counter = 0;
|
|
}
|
|
|
|
// averaging
|
|
if (avg && decimation > 1) {
|
|
sample = samples.sum / decimation;
|
|
samples.sum = 0;
|
|
}
|
|
|
|
// store the sample
|
|
samples.total_saved++;
|
|
|
|
if (bits_per_sample == 8) {
|
|
|
|
data.buffer[samples.total_saved - 1] = sample;
|
|
|
|
// add number of bits.
|
|
data.numbits = samples.total_saved << 3;
|
|
|
|
} else {
|
|
// truncate trailing data
|
|
sample >>= 8 - bits_per_sample;
|
|
sample <<= 8 - bits_per_sample;
|
|
|
|
uint8_t bits_offset = data.numbits & 0x7;
|
|
uint8_t bits_cap = 8 - bits_offset;
|
|
|
|
// write the current byte
|
|
data.buffer[data.numbits >> 3] |= sample >> bits_offset;
|
|
uint32_t numbits = data.numbits + bits_cap;
|
|
|
|
// write the remaining bits to the next byte
|
|
data.buffer[numbits >> 3] |= sample << (bits_cap);
|
|
data.numbits += bits_per_sample;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setup the FPGA to listen for samples. This method downloads the FPGA bitstream
|
|
* if not already loaded, sets divisor and starts up the antenna.
|
|
* @param divisor : 1, 88> 255 or negative ==> 134.8 kHz
|
|
* 0 or 95 ==> 125 kHz
|
|
*
|
|
**/
|
|
void LFSetupFPGAForADC(int divisor, bool reader_field) {
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
if ((divisor == 1) || (divisor < 0) || (divisor > 255)) {
|
|
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_134); //~134kHz
|
|
} else if (divisor == 0) {
|
|
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz
|
|
} else {
|
|
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor);
|
|
}
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | (reader_field ? FPGA_LF_ADC_READER_FIELD : 0));
|
|
|
|
// Connect the A/D to the peak-detected low-frequency path.
|
|
SetAdcMuxFor(ADC_MUXSEL_LOPKD);
|
|
|
|
// Now set up the SSC to get the ADC samples that are now streaming at us.
|
|
FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER);
|
|
|
|
// start a 1.5ticks is 1us
|
|
StartTicks();
|
|
|
|
// 50ms for the resonant antenna to settle.
|
|
if (reader_field) {
|
|
WaitMS(50);
|
|
} else {
|
|
WaitMS(1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Does the sample acquisition. If threshold is specified, the actual sampling
|
|
* is not commenced until the threshold has been reached.
|
|
* This method implements decimation and quantization in order to
|
|
* be able to provide longer sample traces.
|
|
* Uses the following global settings:
|
|
* @param decimation - how much should the signal be decimated. A decimation of N means we keep 1 in N samples, etc.
|
|
* @param bits_per_sample - bits per sample. Max 8, min 1 bit per sample.
|
|
* @param averaging If set to true, decimation will use averaging, so that if e.g. decimation is 3, the sample
|
|
* value that will be used is the average value of the three samples.
|
|
* @param trigger_threshold - a threshold. The sampling won't commence until this threshold has been reached. Set
|
|
* to -1 to ignore threshold.
|
|
* @param verbose - is true, dbprints the status, else no outputs
|
|
* @return the number of bits occupied by the samples.
|
|
*/
|
|
uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, int16_t trigger_threshold,
|
|
bool verbose, uint32_t sample_size, uint32_t cancel_after, int32_t samples_to_skip, bool ledcontrol) {
|
|
|
|
initSampleBuffer(&sample_size); // sample size in bytes
|
|
sample_size <<= 3; // sample size in bits
|
|
sample_size /= bits_per_sample; // sample count
|
|
|
|
if (g_dbglevel >= DBG_DEBUG) {
|
|
printSamples();
|
|
}
|
|
|
|
bool trigger_hit = false;
|
|
uint32_t cancel_counter = 0;
|
|
int16_t checked = 0;
|
|
|
|
while (BUTTON_PRESS() == false) {
|
|
|
|
// only every 4000th times, in order to save time when collecting samples.
|
|
// interruptible only when logging not yet triggered
|
|
if (trigger_hit == false && (checked >= 4000)) {
|
|
if (data_available()) {
|
|
checked = -1;
|
|
break;
|
|
} else {
|
|
checked = 0;
|
|
}
|
|
}
|
|
++checked;
|
|
|
|
WDT_HIT();
|
|
|
|
if (ledcontrol && FPGA_SSC_TX_Ready()) {
|
|
LED_D_ON();
|
|
}
|
|
|
|
if (FPGA_SSC_RX_Ready()) {
|
|
volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value();
|
|
|
|
// (RDV4) Test point 8 (TP8) can be used to trigger oscilloscope
|
|
if (ledcontrol) LED_D_OFF();
|
|
|
|
// threshold either high or low values 128 = center 0. if trigger = 178
|
|
if (trigger_hit == false) {
|
|
if ((trigger_threshold > 0) && (sample < (trigger_threshold + 128)) && (sample > (128 - trigger_threshold))) {
|
|
if (cancel_after > 0) {
|
|
cancel_counter++;
|
|
if (cancel_after == cancel_counter)
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
trigger_hit = true;
|
|
}
|
|
|
|
if (samples_to_skip > 0) {
|
|
samples_to_skip--;
|
|
continue;
|
|
}
|
|
|
|
logSample(sample, decimation, bits_per_sample, avg);
|
|
|
|
if (samples.total_saved >= sample_size) break;
|
|
}
|
|
}
|
|
|
|
if (verbose) {
|
|
if (checked == -1) {
|
|
Dbprintf("lf sampling aborted");
|
|
} else if ((cancel_counter == cancel_after) && (cancel_after > 0)) {
|
|
Dbprintf("lf sampling cancelled after %u", cancel_counter);
|
|
}
|
|
|
|
Dbprintf("Done, saved " _YELLOW_("%d")" out of " _YELLOW_("%d")" seen samples at " _YELLOW_("%d")" bits/sample", samples.total_saved, samples.counter, bits_per_sample);
|
|
}
|
|
|
|
// Ensure that DC offset removal and noise check is performed for any device-side processing
|
|
if (bits_per_sample == 8) {
|
|
// these functions only consider bps==8
|
|
removeSignalOffset(data.buffer, samples.total_saved);
|
|
computeSignalProperties(data.buffer, samples.total_saved);
|
|
}
|
|
return data.numbits;
|
|
}
|
|
|
|
/**
|
|
* @brief Does sample acquisition, ignoring the config values set in the sample_config.
|
|
* This method is typically used by tag-specific readers who just wants to read the samples
|
|
* the normal way
|
|
* @param trigger_threshold
|
|
* @param verbose
|
|
* @return number of bits sampled
|
|
*/
|
|
uint32_t DoAcquisition_default(int trigger_threshold, bool verbose, bool ledcontrol) {
|
|
return DoAcquisition(1, 8, 0, trigger_threshold, verbose, 0, 0, 0, ledcontrol);
|
|
}
|
|
|
|
uint32_t DoAcquisition_config(bool verbose, uint32_t sample_size, bool ledcontrol) {
|
|
return DoAcquisition(config.decimation
|
|
, config.bits_per_sample
|
|
, config.averaging
|
|
, config.trigger_threshold
|
|
, verbose
|
|
, sample_size
|
|
, 0 // cancel_after
|
|
, config.samples_to_skip
|
|
, ledcontrol);
|
|
}
|
|
|
|
uint32_t DoPartialAcquisition(int trigger_threshold, bool verbose, uint32_t sample_size, uint32_t cancel_after, bool ledcontrol) {
|
|
return DoAcquisition(config.decimation
|
|
, config.bits_per_sample
|
|
, config.averaging
|
|
, trigger_threshold
|
|
, verbose
|
|
, sample_size
|
|
, cancel_after
|
|
, 0
|
|
, ledcontrol); // samples to skip
|
|
}
|
|
|
|
static uint32_t ReadLF(bool reader_field, bool verbose, uint32_t sample_size, bool ledcontrol, bool cotag) {
|
|
if (verbose)
|
|
printLFConfig();
|
|
|
|
if (cotag) cotag_start_pulse();
|
|
|
|
LFSetupFPGAForADC(config.divisor, reader_field);
|
|
uint32_t ret = DoAcquisition_config(verbose, sample_size, ledcontrol);
|
|
StopTicks();
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Initializes the FPGA for reader-mode (field on), and acquires the samples.
|
|
* @return number of bits sampled
|
|
**/
|
|
uint32_t SampleLF(bool verbose, uint32_t sample_size, bool ledcontrol, bool cotag) {
|
|
BigBuf_Clear_ext(false);
|
|
return ReadLF(true, verbose, sample_size, ledcontrol, cotag);
|
|
}
|
|
|
|
/**
|
|
* Do LF sampling and send samples to the USB
|
|
*
|
|
* Uses parameters in config. Only bits_per_sample = 8 is working now
|
|
*
|
|
* @param reader_field - true for reading tags, false for sniffing
|
|
* @return sampling result
|
|
**/
|
|
int ReadLF_realtime(bool reader_field, bool cotag, uint32_t sample_limit) {
|
|
// parameters from config and constants
|
|
const uint8_t bits_per_sample = config.bits_per_sample;
|
|
const int16_t trigger_threshold = config.trigger_threshold;
|
|
int32_t samples_to_skip = config.samples_to_skip;
|
|
const uint8_t decimation = config.decimation;
|
|
|
|
const int8_t size_threshold_table[9] = {0, 64, 64, 60, 64, 60, 60, 56, 64};
|
|
const int8_t size_threshold = size_threshold_table[bits_per_sample];
|
|
|
|
// DoAcquisition() start
|
|
uint8_t last_byte = 0;
|
|
uint8_t curr_byte = 0;
|
|
int return_value = PM3_SUCCESS;
|
|
// Total samples streamed so far
|
|
uint32_t total_logged = 0;
|
|
|
|
uint32_t usb_buffer_len = 0, sample_buffer_len;
|
|
usb_get_ep_size(NULL, &usb_buffer_len, NULL);
|
|
sample_buffer_len = usb_buffer_len; // If sample_buffer_len is not specified, a buffer of random length may be requested.
|
|
initSampleBuffer(&sample_buffer_len);
|
|
if (sample_buffer_len != usb_buffer_len) {
|
|
return PM3_EFAILED;
|
|
}
|
|
|
|
bool trigger_hit = false;
|
|
int16_t checked = 0;
|
|
|
|
return_value = async_usb_write_start();
|
|
if (return_value != PM3_SUCCESS) {
|
|
return return_value;
|
|
}
|
|
|
|
BigBuf_Clear_ext(false);
|
|
|
|
if (cotag) cotag_start_pulse();
|
|
|
|
LFSetupFPGAForADC(config.divisor, reader_field);
|
|
|
|
while (BUTTON_PRESS() == false) {
|
|
// only every 4000th times, in order to save time when collecting samples.
|
|
// interruptible only when logging not yet triggered
|
|
if (trigger_hit == false && (checked >= 4000)) {
|
|
if (data_available()) {
|
|
checked = -1;
|
|
break;
|
|
} else {
|
|
checked = 0;
|
|
}
|
|
}
|
|
++checked;
|
|
|
|
WDT_HIT();
|
|
|
|
if (FPGA_SSC_TX_Ready()) {
|
|
LED_D_ON();
|
|
}
|
|
|
|
if (FPGA_SSC_RX_Ready()) {
|
|
volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value();
|
|
|
|
// (RDV4) Test point 8 (TP8) can be used to trigger oscilloscope
|
|
LED_D_OFF();
|
|
|
|
// threshold either high or low values 128 = center 0. if trigger = 178
|
|
if (trigger_hit == false) {
|
|
if ((trigger_threshold > 0) && (sample < (trigger_threshold + 128)) && (sample > (128 - trigger_threshold))) {
|
|
continue;
|
|
}
|
|
trigger_hit = true;
|
|
}
|
|
|
|
if (samples_to_skip > 0) {
|
|
samples_to_skip--;
|
|
continue;
|
|
}
|
|
|
|
logSample(sample, decimation, bits_per_sample, false);
|
|
|
|
// Write to USB FIFO if byte changed
|
|
curr_byte = data.numbits >> 3;
|
|
if (curr_byte > last_byte) {
|
|
async_usb_write_pushByte(data.buffer[last_byte]);
|
|
total_logged++;
|
|
}
|
|
last_byte = curr_byte;
|
|
|
|
if (samples.total_saved == size_threshold) {
|
|
// Request USB transmission and change FIFO bank
|
|
if (async_usb_write_requestWrite() == false) {
|
|
return_value = PM3_EIO;
|
|
goto out;
|
|
}
|
|
|
|
// Reset sample
|
|
last_byte = 0;
|
|
data.numbits = 0;
|
|
samples.counter = size_threshold;
|
|
samples.total_saved = 0;
|
|
|
|
} else if (samples.total_saved == 1) {
|
|
// Check if there is any data from client
|
|
if (data_available_fast()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sample_limit > 0 && total_logged >= sample_limit) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return_value = async_usb_write_stop();
|
|
|
|
out:
|
|
LED_D_OFF();
|
|
|
|
// DoAcquisition() end
|
|
StopTicks();
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
return return_value;
|
|
}
|
|
/**
|
|
* Initializes the FPGA for sniffer-mode (field off), and acquires the samples.
|
|
* @return number of bits sampled
|
|
**/
|
|
uint32_t SniffLF(bool verbose, uint32_t sample_size, bool ledcontrol) {
|
|
BigBuf_Clear_ext(false);
|
|
return ReadLF(false, verbose, sample_size, ledcontrol, false);
|
|
}
|
|
|
|
/**
|
|
* acquisition of T55x7 LF signal. Similar to other LF, but adjusted with @marshmellows thresholds
|
|
* the data is collected in BigBuf.
|
|
**/
|
|
void doT55x7Acquisition(size_t sample_size, bool ledcontrol) {
|
|
|
|
#define T55xx_READ_UPPER_THRESHOLD 128+60 // 60 grph
|
|
#define T55xx_READ_LOWER_THRESHOLD 128-60 // -60 grph
|
|
#define T55xx_READ_TOL 5
|
|
|
|
uint8_t *dest = BigBuf_get_addr();
|
|
uint32_t bufsize = BigBuf_max_traceLen();
|
|
|
|
if (bufsize > sample_size)
|
|
bufsize = sample_size;
|
|
|
|
uint8_t lastSample = 0;
|
|
uint16_t i = 0, skipCnt = 0;
|
|
bool startFound = false;
|
|
bool highFound = false;
|
|
bool lowFound = false;
|
|
|
|
uint16_t checker = 0;
|
|
|
|
if (g_dbglevel >= DBG_DEBUG) {
|
|
Dbprintf("doT55x7Acquisition - after init");
|
|
print_stack_usage();
|
|
}
|
|
|
|
while (skipCnt < 1000 && (i < bufsize)) {
|
|
|
|
if (BUTTON_PRESS())
|
|
break;
|
|
|
|
if (checker == 4000) {
|
|
if (data_available())
|
|
break;
|
|
else
|
|
checker = 0;
|
|
} else {
|
|
++checker;
|
|
}
|
|
|
|
WDT_HIT();
|
|
|
|
if (ledcontrol && FPGA_SSC_TX_Ready()) {
|
|
LED_D_ON();
|
|
}
|
|
|
|
if (FPGA_SSC_RX_Ready()) {
|
|
volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value();
|
|
if (ledcontrol) LED_D_OFF();
|
|
|
|
// skip until the first high sample above threshold
|
|
if (!startFound && sample > T55xx_READ_UPPER_THRESHOLD) {
|
|
highFound = true;
|
|
} else if (!highFound) {
|
|
skipCnt++;
|
|
continue;
|
|
}
|
|
// skip until the first low sample below threshold
|
|
if (!startFound && sample < T55xx_READ_LOWER_THRESHOLD) {
|
|
lastSample = sample;
|
|
lowFound = true;
|
|
} else if (!lowFound) {
|
|
skipCnt++;
|
|
continue;
|
|
}
|
|
|
|
// skip until first high samples begin to change
|
|
if (startFound || sample > T55xx_READ_LOWER_THRESHOLD + T55xx_READ_TOL) {
|
|
// if just found start - recover last sample
|
|
if (startFound == false) {
|
|
dest[i++] = lastSample;
|
|
startFound = true;
|
|
}
|
|
// collect samples
|
|
if (i < bufsize) {
|
|
dest[i++] = sample;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* acquisition of Cotag LF signal. Similart to other LF, since the Cotag has such long datarate RF/384
|
|
* and is Manchester?, we directly gather the manchester data into bigbuff
|
|
**/
|
|
|
|
#define COTAG_T1 384
|
|
#define COTAG_T2 (COTAG_T1 >> 1)
|
|
#define COTAG_ONE_THRESHOLD 127+5
|
|
#define COTAG_ZERO_THRESHOLD 127-5
|
|
#ifndef COTAG_BITS
|
|
#define COTAG_BITS 264
|
|
#endif
|
|
void doCotagAcquisition(void) {
|
|
|
|
uint32_t bufsize = BigBuf_max_traceLen();
|
|
uint8_t *dest = BigBuf_calloc(bufsize);
|
|
|
|
dest[0] = 0;
|
|
|
|
bool firsthigh = false, firstlow = false;
|
|
uint16_t i = 0, noise_counter = 0;
|
|
|
|
uint16_t checker = 0;
|
|
|
|
while ((i < bufsize - 1) && (noise_counter < COTAG_T1 << 1)) {
|
|
|
|
if (BUTTON_PRESS())
|
|
break;
|
|
|
|
if (checker == 4000) {
|
|
if (data_available())
|
|
break;
|
|
else
|
|
checker = 0;
|
|
} else {
|
|
++checker;
|
|
}
|
|
|
|
WDT_HIT();
|
|
|
|
if (FPGA_SSC_RX_Ready()) {
|
|
volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value();
|
|
|
|
// find first peak
|
|
if (firsthigh == false) {
|
|
if (sample < COTAG_ONE_THRESHOLD) {
|
|
noise_counter++;
|
|
continue;
|
|
}
|
|
|
|
noise_counter = 0;
|
|
firsthigh = true;
|
|
}
|
|
|
|
if (firstlow == false) {
|
|
if (sample > COTAG_ZERO_THRESHOLD) {
|
|
noise_counter++;
|
|
continue;
|
|
}
|
|
|
|
noise_counter = 0;
|
|
firstlow = true;
|
|
}
|
|
|
|
if (sample > COTAG_ONE_THRESHOLD) {
|
|
dest[i] = 255;
|
|
++i;
|
|
} else if (sample < COTAG_ZERO_THRESHOLD) {
|
|
dest[i] = 0;
|
|
++i;
|
|
} else {
|
|
dest[i] = dest[i - 1];
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ensure that DC offset removal and noise check is performed for any device-side processing
|
|
removeSignalOffset(dest, i);
|
|
computeSignalProperties(dest, i);
|
|
}
|
|
|
|
uint16_t doCotagAcquisitionManchester(uint8_t *dest, uint16_t destlen) {
|
|
|
|
if (dest == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
dest[0] = 0;
|
|
|
|
bool firsthigh = false, firstlow = false;
|
|
uint8_t curr = 0, prev = 0;
|
|
uint16_t i = 0;
|
|
uint16_t period = 0, checker = 0;
|
|
|
|
while ((i < destlen) && BUTTON_PRESS() == false) {
|
|
|
|
WDT_HIT();
|
|
|
|
if (checker == 4000) {
|
|
if (data_available())
|
|
break;
|
|
else
|
|
checker = 0;
|
|
} else {
|
|
++checker;
|
|
}
|
|
|
|
|
|
if (FPGA_SSC_RX_Ready()) {
|
|
volatile uint8_t sample = (uint8_t)FPGA_SSC_RX_Value();
|
|
|
|
// find first peak
|
|
if (firsthigh == false) {
|
|
if (sample < COTAG_ONE_THRESHOLD) {
|
|
continue;
|
|
}
|
|
firsthigh = true;
|
|
}
|
|
|
|
if (firstlow == false) {
|
|
if (sample > COTAG_ZERO_THRESHOLD) {
|
|
continue;
|
|
}
|
|
firstlow = true;
|
|
}
|
|
|
|
// set sample 255, 0, or previous
|
|
if (sample > COTAG_ONE_THRESHOLD) {
|
|
prev = curr;
|
|
curr = 1;
|
|
} else if (sample < COTAG_ZERO_THRESHOLD) {
|
|
prev = curr;
|
|
curr = 0;
|
|
} else {
|
|
curr = prev;
|
|
}
|
|
|
|
// full T1 periods,
|
|
if (period > 0) {
|
|
--period;
|
|
continue;
|
|
}
|
|
|
|
dest[i] = curr;
|
|
++i;
|
|
period = COTAG_T1;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|