Skip to content

Commit a343f3b

Browse files
committed
add EM4X05 to correct source branch
1 parent e5d615d commit a343f3b

File tree

14 files changed

+2258
-4372
lines changed

14 files changed

+2258
-4372
lines changed

firmware/application/Makefile

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ SRC_FILES += \
3535
$(PROJ_DIR)/rfid/nfctag/lf/utils/circular_buffer.c \
3636
$(PROJ_DIR)/rfid/nfctag/lf/utils/manchester.c \
3737
$(PROJ_DIR)/rfid/nfctag/lf/protocols/em410x.c \
38-
$(PROJ_DIR)/rfid/nfctag/lf/protocols/ioprox.c \
3938
$(PROJ_DIR)/rfid/nfctag/lf/protocols/hidprox.c \
4039
$(PROJ_DIR)/rfid/nfctag/lf/protocols/viking.c \
4140
$(PROJ_DIR)/rfid/nfctag/lf/protocols/wiegand.c \
@@ -341,13 +340,13 @@ ifeq (${CURRENT_DEVICE_TYPE}, ${CHAMELEON_ULTRA})
341340
$(PROJ_DIR)/rfid/reader/hf/rc522.c \
342341
$(PROJ_DIR)/rfid/reader/lf/lf_125khz_radio.c \
343342
$(PROJ_DIR)/rfid/reader/lf/lf_em410x_data.c \
343+
$(PROJ_DIR)/rfid/reader/lf/lf_em4x05_data.c \
344+
$(PROJ_DIR)/rfid/reader/lf/lf_gap.c \
344345
$(PROJ_DIR)/rfid/reader/lf/lf_reader_data.c \
345346
$(PROJ_DIR)/rfid/reader/lf/lf_reader_main.c \
346347
$(PROJ_DIR)/rfid/reader/lf/lf_t55xx_data.c \
347-
$(PROJ_DIR)/rfid/reader/lf/lf_ioprox_data.c \
348348
$(PROJ_DIR)/rfid/reader/lf/lf_hidprox_data.c \
349349
$(PROJ_DIR)/rfid/reader/lf/lf_viking_data.c \
350-
$(PROJ_DIR)/rfid/reader/lf/lf_reader_generic.c \
351350

352351
INC_FOLDERS +=\
353352
${PROJ_DIR}/rfid/reader/ \

firmware/application/src/app_cmd.c

Lines changed: 32 additions & 247 deletions
Large diffs are not rendered by default.

firmware/application/src/data_cmd.h

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,6 @@
7373
#define DATA_CMD_MF1_ENC_NESTED_ACQUIRE (2014)
7474
#define DATA_CMD_MF1_CHECK_KEYS_ON_BLOCK (2015)
7575

76-
#define DATA_CMD_HF14A_SET_FIELD_ON (2100)
77-
#define DATA_CMD_HF14A_SET_FIELD_OFF (2101)
78-
79-
#define DATA_CMD_HF14A_GET_CONFIG (2200)
80-
#define DATA_CMD_HF14A_SET_CONFIG (2201)
81-
8276
//
8377
// ******************************************************************
8478

@@ -90,18 +84,11 @@
9084
//
9185
#define DATA_CMD_EM410X_SCAN (3000)
9286
#define DATA_CMD_EM410X_WRITE_TO_T55XX (3001)
93-
#define DATA_CMD_EM410X_ELECTRA_WRITE_TO_T55XX (3006)
9487
#define DATA_CMD_HIDPROX_SCAN (3002)
9588
#define DATA_CMD_HIDPROX_WRITE_TO_T55XX (3003)
9689
#define DATA_CMD_VIKING_SCAN (3004)
9790
#define DATA_CMD_VIKING_WRITE_TO_T55XX (3005)
98-
#define DATA_CMD_ADC_GENERIC_READ (3009)
99-
#define DATA_CMD_GENERIC_READ (3007)
100-
#define DATA_CMD_CORR_GENERIC_READ (3008)
101-
#define DATA_CMD_IOPROX_SCAN (3010)
102-
#define DATA_CMD_IOPROX_WRITE_TO_T55XX (3011)
103-
#define DATA_CMD_IOPROX_DECODE_RAW (3012)
104-
#define DATA_CMD_IOPROX_COMPOSE_ID (3013)
91+
#define DATA_CMD_EM4X05_SCAN (3010)
10592

10693
//
10794
// ******************************************************************
@@ -150,8 +137,6 @@
150137
#define DATA_CMD_MF0_NTAG_GET_DETECTION_LOG (4035)
151138
#define DATA_CMD_MF0_NTAG_GET_DETECTION_ENABLE (4036)
152139
#define DATA_CMD_MF0_NTAG_GET_EMULATOR_CONFIG (4037)
153-
#define DATA_CMD_MF1_SET_FIELD_OFF_DO_RESET (4038)
154-
#define DATA_CMD_MF1_GET_FIELD_OFF_DO_RESET (4039)
155140
//
156141
// ******************************************************************
157142

@@ -170,7 +155,5 @@
170155
#define DATA_CMD_HIDPROX_GET_EMU_ID (5003)
171156
#define DATA_CMD_VIKING_SET_EMU_ID (5004)
172157
#define DATA_CMD_VIKING_GET_EMU_ID (5005)
173-
#define DATA_CMD_IOPROX_SET_EMU_ID (5008)
174-
#define DATA_CMD_IOPROX_GET_EMU_ID (5009)
175158

176159
#endif
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
#include "pskdemod.h"
2+
3+
#include <stdlib.h>
4+
#include <string.h>
5+
6+
/*
7+
* PSK demodulator for ChameleonUltra LF tag emulation stack.
8+
*
9+
* Signal model
10+
* ============
11+
* The 125 kHz carrier is amplitude-modulated by a square-wave subcarrier.
12+
* The subcarrier frequency is carrier / rf_div (typically rf_div = 4,
13+
* giving a 31.25 kHz subcarrier for Indala/Keri). The GPIOTE edge-
14+
* capture layer gives us the time between successive envelope edges in
15+
* units of 125 kHz carrier cycles.
16+
*
17+
* One data bit spans rf_div subcarrier half-periods (= rf_div² carrier
18+
* cycles). For rf_div=4: 4 half-periods × 4 cycles = 16 cycles per bit.
19+
*
20+
* Phase encoding
21+
* ==============
22+
* A 180° phase shift is visible as a SHORT-SHORT interval pair or a
23+
* LONG interval straddling a half-period boundary.
24+
*
25+
* PSK1 (absolute):
26+
* An odd number of phase shifts in a bit window → bit = '1'.
27+
* Even (including zero) → bit = '0'.
28+
*
29+
* PSK2 (differential BPSK):
30+
* A phase shift at the bit boundary → bit = '1'; no shift → '0'.
31+
* From the demodulator's point of view this is identical to PSK1:
32+
* we just count whether a shift occurred (shift_count & 1).
33+
*
34+
* PSK3 (differential BPSK, inverted):
35+
* Same as PSK2 with the bit sense inverted.
36+
*
37+
* Interval classification
38+
* =======================
39+
* Each call to psk_feed() provides one edge-to-edge interval.
40+
*
41+
* NORMAL ≈ half_period — regular subcarrier edge, no shift
42+
* SHORT ≈ half_period / 2 — first half of a phase-shifted pair
43+
* LONG ≈ 3 * half_period / 2 — phase shift straddled a boundary
44+
* BAD — none of the above; triggers full resync
45+
*
46+
* A phase shift is represented in the signal as either:
47+
* (a) A SHORT followed by another SHORT — together they total one
48+
* nominal half-period but with the subcarrier phase flipped.
49+
* (b) A LONG interval — the subcarrier edge was "pulled" across the
50+
* half-period boundary, equivalent to one shift.
51+
*
52+
* SHORT pairing
53+
* =============
54+
* We use a two-state machine (in_short flag). When the first SHORT
55+
* arrives we set in_short=true and wait. When the paired second SHORT
56+
* arrives we count one half-period and one phase shift. If a non-SHORT
57+
* interval arrives while in_short=true, we declare a desync.
58+
*
59+
* Jitter
60+
* ======
61+
* jitter = max(half_period / PSK_JITTER_DIV, 1).
62+
*
63+
* For rf_div=2 (half_period=2): jitter=1 would make NORMAL and SHORT
64+
* windows overlap. We therefore classify by checking SHORT *before*
65+
* NORMAL only when the interval is strictly less than half_period.
66+
* Concretely: interval < half_period → try SHORT; otherwise try NORMAL.
67+
* This is safe because a genuine SHORT (phase shift mid-period) always
68+
* produces an interval shorter than a genuine NORMAL.
69+
*/
70+
71+
/* -----------------------------------------------------------------------
72+
* Helpers
73+
* --------------------------------------------------------------------- */
74+
75+
typedef enum {
76+
ITYPE_NORMAL,
77+
ITYPE_SHORT,
78+
ITYPE_LONG,
79+
ITYPE_BAD,
80+
} itype_t;
81+
82+
static itype_t classify_interval(const psk_t *p, uint8_t interval) {
83+
uint8_t hp = p->half_period;
84+
uint8_t jitter = p->jitter;
85+
uint8_t short_n = hp / 2;
86+
if (short_n == 0) short_n = 1;
87+
uint8_t long_n = hp + short_n; /* 3/2 × half_period */
88+
89+
/*
90+
* Use interval < hp as the primary discriminant between SHORT and NORMAL.
91+
* This avoids overlap when hp is small (rf_div=2).
92+
*/
93+
if (interval < hp) {
94+
/* Could be SHORT or the low tail of NORMAL jitter */
95+
int16_t ds = (int16_t)interval - (int16_t)short_n;
96+
if (ds < 0) ds = -ds;
97+
if ((uint8_t)ds <= jitter) {
98+
return ITYPE_SHORT;
99+
}
100+
/* Low tail of NORMAL */
101+
int16_t dn = (int16_t)interval - (int16_t)hp;
102+
if (dn < 0) dn = -dn;
103+
if ((uint8_t)dn <= jitter) {
104+
return ITYPE_NORMAL;
105+
}
106+
return ITYPE_BAD;
107+
} else {
108+
/* interval >= hp: could be NORMAL or LONG */
109+
int16_t dn = (int16_t)interval - (int16_t)hp;
110+
if (dn < 0) dn = -dn;
111+
if ((uint8_t)dn <= jitter) {
112+
return ITYPE_NORMAL;
113+
}
114+
int16_t dl = (int16_t)interval - (int16_t)long_n;
115+
if (dl < 0) dl = -dl;
116+
/* Allow 2× jitter for LONG since it covers a wider window */
117+
if ((uint8_t)dl <= (uint8_t)(jitter * 2)) {
118+
return ITYPE_LONG;
119+
}
120+
return ITYPE_BAD;
121+
}
122+
}
123+
124+
/* -----------------------------------------------------------------------
125+
* Public API
126+
* --------------------------------------------------------------------- */
127+
128+
psk_t *psk_alloc(psk_rf_div_t rf_div, psk_mode_t mode) {
129+
psk_t *p = (psk_t *)malloc(sizeof(psk_t));
130+
if (p == NULL) {
131+
return NULL;
132+
}
133+
p->rf_div = rf_div;
134+
p->mode = mode;
135+
p->half_period = (uint8_t)rf_div;
136+
p->jitter = p->half_period / PSK_JITTER_DIV;
137+
if (p->jitter == 0) {
138+
p->jitter = 1;
139+
}
140+
psk_reset(p);
141+
return p;
142+
}
143+
144+
void psk_free(psk_t *p) {
145+
if (p != NULL) {
146+
free(p);
147+
}
148+
}
149+
150+
void psk_reset(psk_t *p) {
151+
p->synced = false;
152+
p->sync_count = 0;
153+
p->clk_count = 0;
154+
p->shift_count = 0;
155+
p->in_short = false;
156+
}
157+
158+
bool psk_feed(psk_t *p, uint8_t interval, bool *bit) {
159+
160+
itype_t itype = classify_interval(p, interval);
161+
162+
if (itype == ITYPE_BAD) {
163+
psk_reset(p);
164+
return false;
165+
}
166+
167+
/* ----------------------------------------------------------------
168+
* SHORT pairing: a phase shift in PSK is a SHORT followed by
169+
* another SHORT. Enforce this contract strictly.
170+
* -------------------------------------------------------------- */
171+
if (p->in_short) {
172+
if (itype != ITYPE_SHORT) {
173+
/* Expected second SHORT but got something else → desync */
174+
psk_reset(p);
175+
return false;
176+
}
177+
/* Paired SHORT received: counts as one half-period + one shift */
178+
p->in_short = false;
179+
itype = ITYPE_NORMAL; /* handle via normal path below */
180+
p->shift_count++;
181+
} else if (itype == ITYPE_SHORT) {
182+
/* First SHORT of a pair — stash and wait */
183+
p->in_short = true;
184+
return false;
185+
}
186+
187+
/* ----------------------------------------------------------------
188+
* Synchronisation: require PSK_SYNC_THRESHOLD clean NORMAL edges
189+
* before emitting bits.
190+
* -------------------------------------------------------------- */
191+
if (!p->synced) {
192+
if (itype == ITYPE_NORMAL) {
193+
p->sync_count++;
194+
if (p->sync_count >= PSK_SYNC_THRESHOLD) {
195+
p->synced = true;
196+
p->clk_count = 0;
197+
p->shift_count = 0;
198+
}
199+
} else {
200+
/* LONG during sync hunt — reset */
201+
p->sync_count = 0;
202+
}
203+
return false;
204+
}
205+
206+
/* ----------------------------------------------------------------
207+
* Bit-window accumulation.
208+
*
209+
* NORMAL: one half-period, no shift (shift already counted above
210+
* for SHORT pairs).
211+
* LONG: straddles one half-period boundary, counts as one
212+
* half-period and one phase shift.
213+
*
214+
* We use >= instead of == when testing clk_count against rf_div so
215+
* a LONG that pushes clk_count over the boundary still fires the
216+
* bit-ready path rather than silently accumulating.
217+
* -------------------------------------------------------------- */
218+
if (itype == ITYPE_LONG) {
219+
p->clk_count++;
220+
p->shift_count++;
221+
} else {
222+
/* ITYPE_NORMAL (includes completed SHORT pairs) */
223+
p->clk_count++;
224+
}
225+
226+
if (p->clk_count < (uint8_t)p->rf_div) {
227+
return false;
228+
}
229+
230+
/* End of bit window — extract bit */
231+
bool phase_shifted = (p->shift_count & 1) != 0;
232+
233+
/* Reset for next bit window */
234+
p->clk_count = 0;
235+
p->shift_count = 0;
236+
237+
switch (p->mode) {
238+
case PSK_MODE_1:
239+
/*
240+
* PSK1 (absolute): odd number of phase shifts → '1'.
241+
*/
242+
*bit = phase_shifted;
243+
break;
244+
245+
case PSK_MODE_2:
246+
/*
247+
* PSK2 (differential BPSK): a phase shift at the bit boundary
248+
* encodes '1', no shift encodes '0'.
249+
* From the demodulator's perspective this is the same as PSK1
250+
* — we detect whether a shift occurred in this window.
251+
*/
252+
*bit = phase_shifted;
253+
break;
254+
255+
case PSK_MODE_3:
256+
/*
257+
* PSK3: same as PSK2 with inverted polarity.
258+
*/
259+
*bit = !phase_shifted;
260+
break;
261+
262+
default:
263+
return false;
264+
}
265+
266+
return true;
267+
}

0 commit comments

Comments
 (0)