Low frequency RFID app [Read stage] (#385)

* App Lfrfid: init
* HAL-resources: add external gpios
* HAL-pwm: fix frequency calculation
* App LFRFID: generic manchester decoder
* App LFRFID: em-marine decoder
* App iButton: fix dwt timing acquire
* App LFRFID: rfid reader
* App LFRFID: temporary read keys on read scene
* App LFRFID: remove atomic bool init.
* App LFRFID: add *.c to build
* App LFRFID: unstable HID decoder
* App LFRFID: HID-26 reading
* HAL OS: disable sleep
* App LFRFID: HID-26 reader: remove debug
* App LFRFID: static data decoder-analyzer
* App LFRFID: very raw Indala decoder
* App LFRFID: multiprotocol reader
* App LFRFID: more reliable HID decoder
* App LFRFID: syntax fix
* App LFRFID: simple read scene
* Gui: force redraw on screen stream connect
* HAL-OS: allow sleep
* App LFRFID: notify api, tune view, tune scene
* App LFRFID: simple rfid emulator
* App LFRFID: more scenes, more reliable EM decoder.
* App LFRFID: format fix
* App LFRFID: warning fix
* Api-hal-resources: add rfid pins, rename external pins
* App LFRFID: remove unused emulator
* App LFRFID: use new gpio hal api
* App accessor: use new ext gpio name
* App LFRFID: remove unused emulator
* App LFRFID: remove debug gpio
* Api-hal-resources: alternate functions init
* Api-hal-rfid: new api
* Api-hal-ibutton: new api
* Api-hal: new headers
* App LFRFID: use new api in reader subroutines
* App LFRFID: use new api in emulator subroutines
* App LFRFID: remove old app
* App LFRFID, App iButton: fix memleak
* Api-hal-rfid: comments
* App LFRFID: pulse joiner helper, it combines pulses of different polarity into one pulse suitable for a timer
* App LFRFID: pulse joiner, now can accept only ne pulse
* App LFRFID: pulse joiner, fixes
* App LFRFID: EM encoder and emulation
* App LFRFID: format fixes
* App LFRFID: emmarine encoder cleanup
* App LFRFID: HID Encoder blank
* App LFRFID: Indala Encoder blank
This commit is contained in:
SG
2021-05-04 23:21:16 +10:00
committed by GitHub
parent dd9bd224be
commit 46bc515c6a
66 changed files with 3278 additions and 514 deletions

View File

@@ -0,0 +1,48 @@
#include "decoder-analyzer.h"
#include <furi.h>
#include <api-hal.h>
bool DecoderAnalyzer::read(uint8_t* _data, uint8_t _data_size) {
bool result = false;
if(ready) {
result = true;
for(size_t i = 0; i < data_size; i++) {
printf("%lu ", data[i]);
if((i + 1) % 8 == 0) printf("\r\n");
}
printf("\r\n--------\r\n");
ready = false;
}
return result;
}
void DecoderAnalyzer::process_front(bool polarity, uint32_t time) {
if(ready) return;
data[data_index] = time;
if(data_index < data_size) {
data_index++;
} else {
data_index = 0;
ready = true;
}
}
DecoderAnalyzer::DecoderAnalyzer() {
data = reinterpret_cast<uint32_t*>(calloc(data_size, sizeof(uint32_t)));
furi_check(data);
data_index = 0;
ready = false;
}
DecoderAnalyzer::~DecoderAnalyzer() {
free(data);
}
void DecoderAnalyzer::reset_state() {
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <stdint.h>
#include <atomic>
class DecoderAnalyzer {
public:
bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time);
DecoderAnalyzer();
~DecoderAnalyzer();
private:
void reset_state();
std::atomic<bool> ready;
static const uint32_t data_size = 2048;
uint32_t data_index = 0;
uint32_t* data;
};

View File

@@ -0,0 +1,172 @@
#include "emmarine.h"
#include "decoder-emmarine.h"
#include <furi.h>
#include <api-hal.h>
constexpr uint32_t clocks_in_us = 64;
constexpr uint32_t short_time = 255 * clocks_in_us;
constexpr uint32_t long_time = 510 * clocks_in_us;
constexpr uint32_t jitter_time = 100 * clocks_in_us;
constexpr uint32_t short_time_low = short_time - jitter_time;
constexpr uint32_t short_time_high = short_time + jitter_time;
constexpr uint32_t long_time_low = long_time - jitter_time;
constexpr uint32_t long_time_high = long_time + jitter_time;
void DecoderEMMarine::reset_state() {
ready = false;
readed_data = 0;
manchester_advance(
manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr);
}
void printEM_raw(uint64_t data) {
// header
for(uint8_t i = 0; i < 9; i++) {
printf("%u ", data & (1LLU << 63) ? 1 : 0);
data = data << 1;
}
printf("\r\n");
// nibbles
for(uint8_t r = 0; r < 11; r++) {
printf(" ");
uint8_t value = 0;
for(uint8_t i = 0; i < 5; i++) {
printf("%u ", data & (1LLU << 63) ? 1 : 0);
if(i < 4) value = (value << 1) | (data & (1LLU << 63) ? 1 : 0);
data = data << 1;
}
printf("0x%X", value);
printf("\r\n");
}
}
void printEM_data(uint64_t data) {
printf("EM ");
// header
for(uint8_t i = 0; i < 9; i++) {
data = data << 1;
}
// nibbles
for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
uint8_t value = 0;
for(uint8_t i = 0; i < 5; i++) {
if(i < 4) value = (value << 1) | (data & (1LLU << 63) ? 1 : 0);
data = data << 1;
}
printf("%X", value);
if(r % 2) printf(" ");
}
printf("\r\n");
}
void copyEM_data(uint64_t data, uint8_t* result, uint8_t result_size) {
furi_assert(result_size >= 5);
uint8_t result_index = 0;
// clean result
memset(result, 0, result_size);
// header
for(uint8_t i = 0; i < 9; i++) {
data = data << 1;
}
// nibbles
uint8_t value = 0;
for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
uint8_t nibble = 0;
for(uint8_t i = 0; i < 5; i++) {
if(i < 4) nibble = (nibble << 1) | (data & (1LLU << 63) ? 1 : 0);
data = data << 1;
}
value = (value << 4) | nibble;
if(r % 2) {
result[result_index] |= value;
result_index++;
value = 0;
}
}
}
bool DecoderEMMarine::read(uint8_t* data, uint8_t data_size) {
bool result = false;
if(ready) {
result = true;
copyEM_data(readed_data, data, data_size);
ready = false;
}
return result;
}
void DecoderEMMarine::process_front(bool polarity, uint32_t time) {
if(ready) return;
if(time < short_time_low) return;
ManchesterEvent event = ManchesterEventReset;
if(time > short_time_low && time < short_time_high) {
if(polarity) {
event = ManchesterEventShortHigh;
} else {
event = ManchesterEventShortLow;
}
} else if(time > long_time_low && time < long_time_high) {
if(polarity) {
event = ManchesterEventLongHigh;
} else {
event = ManchesterEventLongLow;
}
}
if(event != ManchesterEventReset) {
bool data;
bool data_ok =
manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data);
if(data_ok) {
readed_data = (readed_data << 1) | data;
// header and stop bit
if((readed_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return;
// row parity
for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {
uint8_t parity_sum = 0;
for(uint8_t j = 0; j < 5; j++) {
parity_sum += (readed_data >> (EM_FIRST_ROW_POS - i * 5 + j)) & 1;
}
if((parity_sum % 2)) {
return;
}
}
// columns parity
for(uint8_t i = 0; i < 4; i++) {
uint8_t parity_sum = 0;
for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {
parity_sum += (readed_data >> (EM_COLUMN_POS - i + j * 5)) & 1;
}
if((parity_sum % 2)) {
return;
}
}
// checks ok
ready = true;
}
}
}
DecoderEMMarine::DecoderEMMarine() {
reset_state();
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <stdint.h>
#include <atomic>
#include "manchester-decoder.h"
class DecoderEMMarine {
public:
bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time);
DecoderEMMarine();
private:
void reset_state();
uint64_t readed_data = 0;
std::atomic<bool> ready;
ManchesterState manchester_saved_state;
};

View File

@@ -0,0 +1,185 @@
#include "decoder-hid26.h"
#include <api-hal.h>
constexpr uint32_t clocks_in_us = 64;
constexpr uint32_t jitter_time_us = 20;
constexpr uint32_t min_time_us = 64;
constexpr uint32_t max_time_us = 80;
constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us;
constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us;
constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us;
bool DecoderHID26::read(uint8_t* data, uint8_t data_size) {
bool result = false;
furi_assert(data_size >= 3);
if(ready) {
result = true;
data[0] = facility;
data[1] = (uint8_t)(number >> 8);
data[2] = (uint8_t)number;
//printf("HID %02X %02X %02X\r\n", facility, (uint8_t)(number >> 8), (uint8_t)number);
ready = false;
}
return result;
}
void DecoderHID26::process_front(bool polarity, uint32_t time) {
if(ready) return;
if(polarity == true) {
last_pulse_time = time;
} else {
last_pulse_time += time;
if(last_pulse_time > min_time && last_pulse_time < max_time) {
bool pulse;
if(last_pulse_time < mid_time) {
// 6 pulses
pulse = false;
} else {
// 5 pulses
pulse = true;
}
if(last_pulse == pulse) {
pulse_count++;
if(pulse) {
if(pulse_count > 4) {
pulse_count = 0;
store_data(1);
}
} else {
if(pulse_count > 5) {
pulse_count = 0;
store_data(0);
}
}
} else {
if(last_pulse) {
if(pulse_count > 2) {
store_data(1);
}
} else {
if(pulse_count > 3) {
store_data(0);
}
}
pulse_count = 0;
last_pulse = pulse;
}
}
}
}
DecoderHID26::DecoderHID26() {
reset_state();
}
void DecoderHID26::store_data(bool data) {
stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1);
stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1);
stored_data[2] = (stored_data[2] << 1) | data;
validate_stored_data();
}
void DecoderHID26::validate_stored_data() {
// packet preamble
// raw data
if(*(reinterpret_cast<uint8_t*>(stored_data) + 3) != 0x1D) {
return;
}
// encoded company/oem
// coded with 01 = 0, 10 = 1 transitions
// stored in word 0
if((*stored_data >> 10 & 0x3FFF) != 0x1556) {
return;
}
// encoded format/length
// coded with 01 = 0, 10 = 1 transitions
// stored in word 0 and word 1
if((((*stored_data & 0x3FF) << 12) | ((*(stored_data + 1) >> 20) & 0xFFF)) != 0x155556) {
return;
}
// data decoding
uint32_t result = 0;
// decode from word 1
// coded with 01 = 0, 10 = 1 transitions
for(int8_t i = 9; i >= 0; i--) {
switch((*(stored_data + 1) >> (2 * i)) & 0b11) {
case 0b01:
result = (result << 1) | 0;
break;
case 0b10:
result = (result << 1) | 1;
break;
default:
return;
break;
}
}
// decode from word 2
// coded with 01 = 0, 10 = 1 transitions
for(int8_t i = 15; i >= 0; i--) {
switch((*(stored_data + 2) >> (2 * i)) & 0b11) {
case 0b01:
result = (result << 1) | 0;
break;
case 0b10:
result = (result << 1) | 1;
break;
default:
return;
break;
}
}
// store decoded data
facility = result >> 17;
number = result >> 1;
// trailing parity (odd) test
uint8_t parity_sum = 0;
for(int8_t i = 0; i < 13; i++) {
if(((result >> i) & 1) == 1) {
parity_sum++;
}
}
if((parity_sum % 2) != 1) {
return;
}
// leading parity (even) test
parity_sum = 0;
for(int8_t i = 13; i < 26; i++) {
if(((result >> i) & 1) == 1) {
parity_sum++;
}
}
if((parity_sum % 2) == 1) {
return;
}
ready = true;
}
void DecoderHID26::reset_state() {
last_pulse = false;
pulse_count = 0;
ready = false;
last_pulse_time = 0;
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include <stdint.h>
#include <atomic>
class DecoderHID26 {
public:
bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time);
DecoderHID26();
private:
uint32_t last_pulse_time = 0;
bool last_pulse;
uint8_t pulse_count;
uint32_t stored_data[3] = {0, 0, 0};
void store_data(bool data);
void validate_stored_data();
uint8_t facility = 0;
uint16_t number = 0;
std::atomic<bool> ready;
void reset_state();
};

View File

@@ -0,0 +1,170 @@
#include "decoder-indala.h"
#include <api-hal.h>
constexpr uint32_t clocks_in_us = 64;
constexpr uint32_t min_time_us = 25 * clocks_in_us;
constexpr uint32_t mid_time_us = 45 * clocks_in_us;
constexpr uint32_t max_time_us = 90 * clocks_in_us;
bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
bool result = false;
if(ready) {
result = true;
printf("IND %02X %02X %02X\r\n", facility, (uint8_t)(number >> 8), (uint8_t)number);
ready = false;
}
return result;
}
void DecoderIndala::process_front(bool polarity, uint32_t time) {
if(ready) return;
if(polarity == false) {
last_pulse_time = time;
} else {
last_pulse_time += time;
pulse_count++;
if(last_pulse_time > min_time_us && last_pulse_time < max_time_us) {
if(last_pulse_time > mid_time_us) {
bool last_data = !(readed_data & 1);
pulse_count = 0;
readed_data = (readed_data << 1) | last_data;
verify();
} else if((pulse_count % 16) == 0) {
bool last_data = readed_data & 1;
pulse_count = 0;
readed_data = (readed_data << 1) | last_data;
verify();
}
}
}
}
DecoderIndala::DecoderIndala() {
}
void DecoderIndala::reset_state() {
}
void DecoderIndala::verify() {
// verify inverse
readed_data = ~readed_data;
verify_inner();
// verify normal
readed_data = ~readed_data;
verify_inner();
}
typedef union {
uint64_t raw;
struct __attribute__((packed)) {
uint8_t static0 : 3;
uint8_t checksum : 2;
uint8_t static1 : 2;
uint8_t y14 : 1;
uint8_t x8 : 1;
uint8_t x1 : 1;
uint8_t y13 : 1;
uint8_t static2 : 1;
uint8_t y12 : 1;
uint8_t x6 : 1;
uint8_t y5 : 1;
uint8_t y8 : 1;
uint8_t y15 : 1;
uint8_t x2 : 1;
uint8_t x5 : 1;
uint8_t x4 : 1;
uint8_t y9 : 1;
uint8_t y2 : 1;
uint8_t x3 : 1;
uint8_t y3 : 1;
uint8_t y1 : 1;
uint8_t y16 : 1;
uint8_t y4 : 1;
uint8_t x7 : 1;
uint8_t p2 : 1;
uint8_t y11 : 1;
uint8_t y6 : 1;
uint8_t y7 : 1;
uint8_t p1 : 1;
uint8_t y10 : 1;
uint32_t preamble : 30;
};
} IndalaFormat;
void DecoderIndala::verify_inner() {
IndalaFormat id;
id.raw = readed_data;
// preamble
//if((data >> 34) != 0b000000000000000000000000000001) return;
if(id.preamble != 1) return;
// static data bits
//if((data & 0b100001100111) != 0b101) return;
if(id.static2 != 0 && id.static1 != 0 && id.static0 != 0b101) return;
// Indala checksum
uint8_t sum_to_check = id.y2 + id.y4 + id.y7 + id.y8 + id.y10 + id.y11 + id.y14 + id.y16;
if(sum_to_check % 2 == 0) {
if(id.checksum != 0b10) return;
} else {
if(id.checksum != 0b01) return;
}
// read facility number
facility = (id.x1 << 7) + (id.x2 << 6) + (id.x3 << 5) + (id.x4 << 4) + (id.x5 << 3) +
(id.x6 << 2) + (id.x7 << 1) + (id.x8 << 0);
// read serial number
number = (id.y1 << 15) + (id.y2 << 14) + (id.y3 << 13) + (id.y4 << 12) + (id.y5 << 11) +
(id.y6 << 10) + (id.y7 << 9) + (id.y8 << 8) + (id.y9 << 7) + (id.y10 << 6) +
(id.y11 << 5) + (id.y12 << 4) + (id.y13 << 3) + (id.y14 << 2) + (id.y15 << 1) +
(id.y16 << 0);
// Wiegand checksum left
sum_to_check = 0;
for(int8_t i = 0; i < 8; i--) {
if((facility >> i) & 1) {
sum_to_check += 1;
}
}
for(int8_t i = 0; i < 4; i--) {
if((number >> i) & 1) {
sum_to_check += 1;
}
}
if(id.p1) {
sum_to_check += 1;
}
if((sum_to_check % 2) == 1) return;
// Wiegand checksum right
sum_to_check = 0;
for(int8_t i = 0; i < 12; i--) {
if((number >> (i + 4)) & 1) {
sum_to_check += 1;
}
}
if(id.p2) {
sum_to_check += 1;
}
if((sum_to_check % 2) != 1) return;
ready = true;
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <stdint.h>
#include <limits.h>
#include <atomic>
class DecoderIndala {
public:
bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time);
DecoderIndala();
private:
void reset_state();
void verify();
void verify_inner();
uint32_t last_pulse_time = 0;
uint32_t pulse_count = 0;
uint32_t overall_pulse_count = 0;
uint64_t readed_data = 0;
std::atomic<bool> ready;
uint8_t facility = 0;
uint16_t number = 0;
};

View File

@@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#define EM_HEADER_POS 55
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
#define EM_FIRST_ROW_POS 50
#define EM_ROW_COUNT 10
#define EM_COLUMN_POS 4
#define EM_STOP_POS 0
#define EM_STOP_MASK (0x1LLU << EM_STOP_POS)
#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)
#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)

View File

@@ -0,0 +1,58 @@
#include "encoder-emmarine.h"
#include <furi.h>
void EncoderEM::init(const uint8_t* data, const uint8_t data_size) {
furi_check(data_size == 5);
// header
card_data = 0b111111111;
// data
for(uint8_t i = 0; i < 5; i++) {
write_nibble(false, data[i]);
write_nibble(true, data[i]);
}
// column parity and stop bit
uint8_t parity_sum;
for(uint8_t c = 0; c < 4; c++) {
parity_sum = 0;
for(uint8_t i = 1; i <= 10; i++) {
uint8_t parity_bit = (card_data >> (i * 5 - 1)) & 1;
parity_sum += parity_bit;
}
card_data = (card_data << 1) | ((parity_sum % 2) & 1);
}
// stop bit
card_data = (card_data << 1) | 0;
card_data_index = 0;
}
void EncoderEM::write_nibble(bool low_nibble, uint8_t data) {
uint8_t parity_sum = 0;
uint8_t start = 0;
if(!low_nibble) start = 4;
for(int8_t i = (start + 3); i >= start; i--) {
parity_sum += (data >> i) & 1;
card_data = (card_data << 1) | ((data >> i) & 1);
}
card_data = (card_data << 1) | ((parity_sum % 2) & 1);
}
// data transmitted as manchester encoding
// 0 - high2low
// 1 - low2high
void EncoderEM::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
*period = clocks_per_bit;
*pulse = clocks_per_bit / 2;
*polarity = (card_data >> (63 - card_data_index)) & 1;
card_data_index++;
if(card_data_index >= 64) {
card_data_index = 0;
}
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "encoder-generic.h"
class EncoderEM : public EncoderGeneric {
public:
/**
* @brief init data to emulate
*
* @param data 1 byte FC, next 4 byte SN
* @param data_size must be 5
*/
void init(const uint8_t* data, const uint8_t data_size) final;
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
private:
// clock pulses per bit
static const uint8_t clocks_per_bit = 64;
uint64_t card_data;
uint8_t card_data_index;
void write_nibble(bool low_nibble, uint8_t data);
};

View File

@@ -0,0 +1,27 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
class EncoderGeneric {
public:
/**
* @brief init encoder
*
* @param data data array
* @param data_size data array size
*/
virtual void init(const uint8_t* data, const uint8_t data_size) = 0;
/**
* @brief Get the next timer pulse
*
* @param polarity pulse polarity true = high2low, false = low2high
* @param period overall period time in timer clicks
* @param pulse pulse time in timer clicks
*/
virtual void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) = 0;
virtual ~EncoderGeneric(){};
private:
};

View File

@@ -0,0 +1,13 @@
#include "encoder-hid.h"
#include <furi.h>
void EncoderHID::init(const uint8_t* data, const uint8_t data_size) {
card_data = 0b1010000000000000000000000000000010011101111110011001001001010010;
card_data_index = 0;
}
void EncoderHID::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
*period = 100;
*pulse = 50;
*polarity = true;
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "encoder-generic.h"
class EncoderHID : public EncoderGeneric {
public:
/**
* @brief init data to emulate
*
* @param data 1 byte FC, next 2 byte SN
* @param data_size must be 3
*/
void init(const uint8_t* data, const uint8_t data_size) final;
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
private:
uint64_t card_data;
uint8_t card_data_index;
};

View File

@@ -0,0 +1,27 @@
#include "encoder-indala.h"
#include <furi.h>
void EncoderIndala::init(const uint8_t* data, const uint8_t data_size) {
card_data = 0b1010000000000000000000000000000010011101111110011001001001010010;
last_polarity = card_data & 1;
card_data_index = 0;
}
void EncoderIndala::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
bool new_bit = (card_data >> (63 - card_data_index)) & 1;
*period = 2;
*pulse = 1;
*polarity = (new_bit != last_polarity);
bit_clock_index++;
if(bit_clock_index >= clock_per_bit) {
bit_clock_index = 0;
last_polarity = *polarity;
card_data_index++;
if(card_data_index >= 64) {
card_data_index = 0;
}
}
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "encoder-generic.h"
class EncoderIndala : public EncoderGeneric {
public:
/**
* @brief init data to emulate
*
* @param data indala raw data
* @param data_size must be 5
*/
void init(const uint8_t* data, const uint8_t data_size) final;
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
private:
uint64_t card_data;
uint8_t card_data_index;
uint8_t bit_clock_index;
bool last_polarity;
static const uint8_t clock_per_bit = 16;
};

View File

@@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
static const uint8_t LFRFID_KEY_SIZE = 8;
enum class LfrfidKeyType : uint8_t {
KeyEmarine,
KeyHID,
};

View File

@@ -0,0 +1,34 @@
#include "manchester-decoder.h"
#include <stdint.h>
static const uint8_t transitions[] = {0b00000001, 0b10010001, 0b10011011, 0b11111011};
static const ManchesterState manchester_reset_state = ManchesterStateMid1;
bool manchester_advance(
ManchesterState state,
ManchesterEvent event,
ManchesterState* next_state,
bool* data) {
bool result = false;
ManchesterState new_state;
if(event == ManchesterEventReset) {
new_state = manchester_reset_state;
} else {
new_state = transitions[state] >> event & 0x3;
if(new_state == state) {
new_state = manchester_reset_state;
} else {
if(new_state == ManchesterStateMid0) {
*data = false;
result = true;
} else if(new_state == ManchesterStateMid1) {
*data = true;
result = true;
}
}
}
*next_state = new_state;
return result;
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
ManchesterEventShortLow = 0,
ManchesterEventShortHigh = 2,
ManchesterEventLongLow = 4,
ManchesterEventLongHigh = 6,
ManchesterEventReset = 8
} ManchesterEvent;
typedef enum {
ManchesterStateStart1 = 0,
ManchesterStateMid1 = 1,
ManchesterStateMid0 = 2,
ManchesterStateStart0 = 3
} ManchesterState;
bool manchester_advance(
ManchesterState state,
ManchesterEvent event,
ManchesterState* next_state,
bool* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,95 @@
#include "pulse-joiner.h"
#include <furi.h>
bool PulseJoiner::push_pulse(bool polarity, uint16_t period, uint16_t pulse) {
bool result = false;
furi_check((pulse_index + 1) < pulse_max);
if(polarity == false && pulse_index == 0) {
// first negative pulse is ommited
} else {
pulses[pulse_index].polarity = polarity;
pulses[pulse_index].time = pulse;
pulse_index++;
}
if(period > pulse) {
pulses[pulse_index].polarity = !polarity;
pulses[pulse_index].time = period - pulse;
pulse_index++;
}
if(pulse_index >= 4) {
// we know that first pulse is always high
// so we wait 2 edges, hi2low and next low2hi
uint8_t edges_count = 0;
bool last_polarity = pulses[0].polarity;
for(uint8_t i = 1; i < pulse_index; i++) {
if(pulses[i].polarity != last_polarity) {
edges_count++;
last_polarity = pulses[i].polarity;
}
}
if(edges_count >= 2) {
result = true;
}
}
return result;
}
void PulseJoiner::pop_pulse(uint16_t* period, uint16_t* pulse) {
furi_check(pulse_index <= (pulse_max + 1));
uint16_t tmp_period = 0;
uint16_t tmp_pulse = 0;
uint8_t edges_count = 0;
bool last_polarity = pulses[0].polarity;
uint8_t next_fist_pulse = 0;
for(uint8_t i = 0; i < pulse_max; i++) {
// count edges
if(pulses[i].polarity != last_polarity) {
edges_count++;
last_polarity = pulses[i].polarity;
}
// wait for 2 edges
if(edges_count == 2) {
next_fist_pulse = i;
break;
}
// sum pulse time
if(pulses[i].polarity) {
tmp_period += pulses[i].time;
tmp_pulse += pulses[i].time;
} else {
tmp_period += pulses[i].time;
}
pulse_index--;
}
*period = tmp_period;
*pulse = tmp_pulse;
// remove counted periods and shift data
for(uint8_t i = 0; i < pulse_max; i++) {
if((next_fist_pulse + i) < pulse_max) {
pulses[i].polarity = pulses[next_fist_pulse + i].polarity;
pulses[i].time = pulses[next_fist_pulse + i].time;
} else {
break;
}
}
}
PulseJoiner::PulseJoiner() {
for(uint8_t i = 0; i < pulse_max; i++) {
pulses[i] = {false, 0};
}
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include "stdint.h"
class PulseJoiner {
public:
/**
* @brief Push timer pulse. First negative pulse is ommited.
*
* @param polarity pulse polarity: true = high2low, false = low2high
* @param period overall period time in timer clicks
* @param pulse pulse time in timer clicks
*
* @return true - next pulse can and must be popped immediatly
*/
bool push_pulse(bool polarity, uint16_t period, uint16_t pulse);
/**
* @brief Get the next timer pulse. Call only if push_pulse returns true.
*
* @param period overall period time in timer clicks
* @param pulse pulse time in timer clicks
*/
void pop_pulse(uint16_t* period, uint16_t* pulse);
PulseJoiner();
private:
struct Pulse {
bool polarity;
uint16_t time;
};
uint8_t pulse_index = 0;
static const uint8_t pulse_max = 6;
Pulse pulses[pulse_max];
};

View File

@@ -0,0 +1,134 @@
#include "rfid-reader.h"
#include <furi.h>
#include <api-hal.h>
#include <stm32wbxx_ll_cortex.h>
#include <tim.h>
extern COMP_HandleTypeDef hcomp1;
/**
* @brief private violation assistant for RfidReader
*/
struct RfidReaderAccessor {
static void decode(RfidReader& rfid_reader, bool polarity) {
rfid_reader.decode(polarity);
}
};
void RfidReader::decode(bool polarity) {
uint32_t current_dwt_value = DWT->CYCCNT;
switch(type) {
case Type::Normal:
decoder_em.process_front(polarity, current_dwt_value - last_dwt_value);
decoder_hid26.process_front(polarity, current_dwt_value - last_dwt_value);
//decoder_indala.process_front(polarity, current_dwt_value - last_dwt_value);
//decoder_analyzer.process_front(polarity, current_dwt_value - last_dwt_value);
last_dwt_value = current_dwt_value;
break;
case Type::Indala:
break;
}
}
static void comparator_trigger_callback(void* hcomp, void* comp_ctx) {
COMP_HandleTypeDef* _hcomp = static_cast<COMP_HandleTypeDef*>(hcomp);
RfidReader* _this = static_cast<RfidReader*>(comp_ctx);
if(hcomp == &hcomp1) {
RfidReaderAccessor::decode(
*_this, (HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_HIGH));
}
}
RfidReader::RfidReader() {
}
void RfidReader::start(Type _type) {
type = _type;
start_gpio();
switch(type) {
case Type::Normal:
start_timer();
break;
case Type::Indala:
start_timer_indala();
break;
}
start_comparator();
}
void RfidReader::stop() {
stop_gpio();
stop_timer();
stop_comparator();
}
bool RfidReader::read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size) {
bool result = false;
if(decoder_em.read(data, data_size)) {
*type = LfrfidKeyType::KeyEmarine;
result = true;
}
if(decoder_hid26.read(data, data_size)) {
*type = LfrfidKeyType::KeyHID;
result = true;
}
//decoder_indala.read(NULL, 0);
//decoder_analyzer.read(NULL, 0);
return result;
}
void RfidReader::start_comparator(void) {
api_interrupt_add(comparator_trigger_callback, InterruptTypeComparatorTrigger, this);
last_dwt_value = DWT->CYCCNT;
hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_2VREFINT;
hcomp1.Init.InputPlus = COMP_INPUT_PLUS_IO1;
hcomp1.Init.OutputPol = COMP_OUTPUTPOL_NONINVERTED;
hcomp1.Init.Hysteresis = COMP_HYSTERESIS_LOW;
hcomp1.Init.BlankingSrce = COMP_BLANKINGSRC_NONE;
hcomp1.Init.Mode = COMP_POWERMODE_MEDIUMSPEED;
hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE;
hcomp1.Init.TriggerMode = COMP_TRIGGERMODE_IT_RISING_FALLING;
if(HAL_COMP_Init(&hcomp1) != HAL_OK) {
Error_Handler();
}
HAL_COMP_Start(&hcomp1);
}
void RfidReader::start_timer(void) {
api_hal_rfid_tim_read(125000, 0.5);
api_hal_rfid_tim_read_start();
}
void RfidReader::start_timer_indala(void) {
api_hal_rfid_tim_read(62500, 0.25);
api_hal_rfid_tim_read_start();
}
void RfidReader::start_gpio(void) {
api_hal_rfid_pins_read();
}
void RfidReader::stop_comparator(void) {
HAL_COMP_Stop(&hcomp1);
api_interrupt_remove(comparator_trigger_callback, InterruptTypeComparatorTrigger);
}
void RfidReader::stop_timer(void) {
api_hal_rfid_tim_read_stop();
api_hal_rfid_tim_reset();
}
void RfidReader::stop_gpio(void) {
api_hal_rfid_pins_reset();
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include "decoder-analyzer.h"
#include "decoder-emmarine.h"
#include "decoder-hid26.h"
#include "decoder-indala.h"
#include "key-info.h"
class RfidReader {
public:
enum class Type : uint8_t {
Normal,
Indala,
};
RfidReader();
void start(Type type);
void stop();
bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size);
private:
friend struct RfidReaderAccessor;
//DecoderAnalyzer decoder_analyzer;
DecoderEMMarine decoder_em;
DecoderHID26 decoder_hid26;
DecoderIndala decoder_indala;
uint32_t last_dwt_value;
void start_comparator(void);
void start_timer(void);
void start_timer_indala(void);
void start_gpio(void);
void stop_comparator(void);
void stop_timer(void);
void stop_gpio(void);
void decode(bool polarity);
Type type = Type::Normal;
};

View File

@@ -0,0 +1,404 @@
#include "rfid-timer-emulator.h"
extern TIM_HandleTypeDef htim1;
/*
static uint16_t times_index = 0;
constexpr uint16_t hid_237_34672_count = 528;
constexpr uint8_t hid_237_34672[hid_237_34672_count] = {
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8,
8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8,
10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10,
10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8,
8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8,
8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8,
8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10,
10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8,
8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8,
10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10,
10, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8,
8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10,
10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10,
10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10,
10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10,
8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 10,
10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10,
10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8,
};
static void callback_hid(void* _hw, void* ctx) {
//RfidTimerEmulator* _this = static_cast<RfidTimerEmulator*>(ctx);
TIM_HandleTypeDef* hw = static_cast<TIM_HandleTypeDef*>(_hw);
if(hw == &htim1) {
hw->Instance->ARR = hid_237_34672[times_index] - 1;
hw->Instance->CCR1 = hid_237_34672[times_index] / 2; // - 1
times_index++;
if(times_index >= hid_237_34672_count) {
times_index = 0;
}
}
}
typedef struct {
uint8_t arr;
uint8_t ccr;
} TimerTick;
constexpr TimerTick indala_data[] = {
{.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1},
{.arr = 2, .ccr = 1},
};
constexpr uint16_t indala_size = sizeof(indala_data) / sizeof(TimerTick);
static void callback_indala(void* _hw, void* ctx) {
//RfidTimerEmulator* _this = static_cast<RfidTimerEmulator*>(ctx);
TIM_HandleTypeDef* hw = static_cast<TIM_HandleTypeDef*>(_hw);
if(hw == &htim1) {
hw->Instance->ARR = indala_data[times_index].arr - 1;
hw->Instance->CCR1 = indala_data[times_index].ccr;
times_index++;
if(times_index >= indala_size) {
times_index = 0;
}
}
}
*/
RfidTimerEmulator::RfidTimerEmulator() {
}
RfidTimerEmulator::~RfidTimerEmulator() {
std::map<Type, EncoderGeneric*>::iterator it;
for(it = encoders.begin(); it != encoders.end(); ++it) {
delete it->second;
encoders.erase(it);
}
}
void RfidTimerEmulator::start(Type type) {
if(encoders.count(type)) {
current_encoder = encoders.find(type)->second;
uint8_t em_data[5] = {0x53, 0x00, 0x5F, 0xB3, 0xC2};
switch(type) {
case Type::EM:
current_encoder->init(em_data, 5);
break;
case Type::HID:
current_encoder->init(nullptr, 3);
break;
case Type::Indala:
current_encoder->init(nullptr, 5);
break;
}
api_hal_rfid_tim_emulate(125000);
api_hal_rfid_pins_emulate();
api_interrupt_add(timer_update_callback, InterruptTypeTimerUpdate, this);
for(size_t i = WWDG_IRQn; i <= DMAMUX1_OVR_IRQn; i++) {
HAL_NVIC_SetPriority(static_cast<IRQn_Type>(i), 15, 0);
}
HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);
api_hal_rfid_tim_emulate_start();
} else {
// not found
}
}
void RfidTimerEmulator::stop() {
api_hal_rfid_tim_emulate_stop();
api_interrupt_remove(timer_update_callback, InterruptTypeTimerUpdate);
}
void RfidTimerEmulator::emulate() {
}
void RfidTimerEmulator::timer_update_callback(void* _hw, void* ctx) {
RfidTimerEmulator* _this = static_cast<RfidTimerEmulator*>(ctx);
TIM_HandleTypeDef* hw = static_cast<TIM_HandleTypeDef*>(_hw);
if(hw == &LFRFID_TIM) {
bool result;
bool polarity;
uint16_t period;
uint16_t pulse;
do {
_this->current_encoder->get_next(&polarity, &period, &pulse);
result = _this->pulse_joiner.push_pulse(polarity, period, pulse);
} while(result == false);
_this->pulse_joiner.pop_pulse(&period, &pulse);
hw->Instance->ARR = period - 1;
hw->Instance->CCR1 = pulse;
}
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <api-hal.h>
#include "key-info.h"
#include "encoder-generic.h"
#include "encoder-emmarine.h"
#include "encoder-hid.h"
#include "encoder-indala.h"
#include "pulse-joiner.h"
#include <map>
class RfidTimerEmulator {
public:
enum class Type : uint8_t {
EM,
HID,
Indala,
};
RfidTimerEmulator();
~RfidTimerEmulator();
void start(Type type);
void stop();
void emulate();
private:
EncoderGeneric* current_encoder = nullptr;
std::map<Type, EncoderGeneric*> encoders = {
{Type::EM, new EncoderEM()},
{Type::HID, new EncoderHID()},
{Type::Indala, new EncoderIndala()},
};
PulseJoiner pulse_joiner;
static void timer_update_callback(void* _hw, void* ctx);
};