]>
Commit | Line | Data |
---|---|---|
31abe49f MHS |
1 | //----------------------------------------------------------------------------- |
2 | // This code is licensed to you under the terms of the GNU GPL, version 2 or, | |
3 | // at your option, any later version. See the LICENSE.txt file for the text of | |
4 | // the license. | |
5 | //----------------------------------------------------------------------------- | |
6 | // Miscellaneous routines for low frequency sampling. | |
7 | //----------------------------------------------------------------------------- | |
8 | ||
31abe49f | 9 | #include "lfsampling.h" |
10a8875c | 10 | |
507afbf3 | 11 | /* |
12 | Default LF config is set to: | |
13 | decimation = 1 (we keep 1 out of 1 samples) | |
14 | bits_per_sample = 8 | |
15 | averaging = YES | |
16 | divisor = 95 (125khz) | |
17 | trigger_threshold = 0 | |
18 | */ | |
0cd2a41a | 19 | sample_config config = { 1, 8, 1, 95, 0 } ; |
31abe49f | 20 | |
24c49d36 | 21 | void printConfig() { |
7838f4be | 22 | Dbprintf("LF Sampling config: "); |
5f5b83b7 | 23 | Dbprintf(" [q] divisor: %d (%d KHz)", config.divisor, 12000 / (config.divisor+1)); |
31abe49f MHS |
24 | Dbprintf(" [b] bps: %d ", config.bits_per_sample); |
25 | Dbprintf(" [d] decimation: %d ", config.decimation); | |
507afbf3 | 26 | Dbprintf(" [a] averaging: %s ", (config.averaging) ? "Yes" : "No"); |
31abe49f MHS |
27 | Dbprintf(" [t] trigger threshold: %d ", config.trigger_threshold); |
28 | } | |
29 | ||
31abe49f MHS |
30 | /** |
31 | * Called from the USB-handler to set the sampling configuration | |
32 | * The sampling config is used for std reading and snooping. | |
33 | * | |
34 | * Other functions may read samples and ignore the sampling config, | |
35 | * such as functions to read the UID from a prox tag or similar. | |
36 | * | |
37 | * Values set to '0' implies no change (except for averaging) | |
38 | * @brief setSamplingConfig | |
39 | * @param sc | |
40 | */ | |
24c49d36 | 41 | void setSamplingConfig(sample_config *sc) { |
31abe49f | 42 | if(sc->divisor != 0) config.divisor = sc->divisor; |
9974991e | 43 | if(sc->bits_per_sample != 0) config.bits_per_sample = sc->bits_per_sample; |
9974991e | 44 | if(sc->trigger_threshold != -1) config.trigger_threshold = sc->trigger_threshold; |
507afbf3 | 45 | |
46 | config.decimation = (sc->decimation != 0) ? sc->decimation : 1; | |
9974991e | 47 | config.averaging = sc->averaging; |
31abe49f | 48 | if(config.bits_per_sample > 8) config.bits_per_sample = 8; |
31abe49f MHS |
49 | |
50 | printConfig(); | |
51 | } | |
52 | ||
24c49d36 | 53 | sample_config* getSamplingConfig() { |
31abe49f MHS |
54 | return &config; |
55 | } | |
10a8875c | 56 | |
53d5dc64 | 57 | struct BitstreamOut { |
31abe49f MHS |
58 | uint8_t * buffer; |
59 | uint32_t numbits; | |
60 | uint32_t position; | |
53d5dc64 | 61 | }; |
31abe49f | 62 | |
31abe49f MHS |
63 | /** |
64 | * @brief Pushes bit onto the stream | |
65 | * @param stream | |
66 | * @param bit | |
67 | */ | |
24c49d36 | 68 | void pushBit( BitstreamOut* stream, uint8_t bit) { |
31abe49f MHS |
69 | int bytepos = stream->position >> 3; // divide by 8 |
70 | int bitpos = stream->position & 7; | |
71 | *(stream->buffer+bytepos) |= (bit > 0) << (7 - bitpos); | |
72 | stream->position++; | |
73 | stream->numbits++; | |
74 | } | |
10a8875c | 75 | |
31abe49f MHS |
76 | /** |
77 | * Setup the FPGA to listen for samples. This method downloads the FPGA bitstream | |
78 | * if not already loaded, sets divisor and starts up the antenna. | |
79 | * @param divisor : 1, 88> 255 or negative ==> 134.8 KHz | |
80 | * 0 or 95 ==> 125 KHz | |
81 | * | |
82 | **/ | |
24c49d36 | 83 | void LFSetupFPGAForADC(int divisor, bool lf_field) { |
31abe49f MHS |
84 | FpgaDownloadAndGo(FPGA_BITSTREAM_LF); |
85 | if ( (divisor == 1) || (divisor < 0) || (divisor > 255) ) | |
86 | FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz | |
87 | else if (divisor == 0) | |
88 | FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz | |
89 | else | |
90 | FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor); | |
91 | ||
92 | FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | (lf_field ? FPGA_LF_ADC_READER_FIELD : 0)); | |
93 | ||
94 | // Connect the A/D to the peak-detected low-frequency path. | |
95 | SetAdcMuxFor(GPIO_MUXSEL_LOPKD); | |
8255e1a6 | 96 | // 50ms for the resonant antenna to settle. |
31abe49f MHS |
97 | SpinDelay(50); |
98 | // Now set up the SSC to get the ADC samples that are now streaming at us. | |
99 | FpgaSetupSsc(); | |
24c49d36 | 100 | // start a 1.5ticks is 1us |
101 | StartTicks(); | |
31abe49f MHS |
102 | } |
103 | ||
31abe49f MHS |
104 | /** |
105 | * Does the sample acquisition. If threshold is specified, the actual sampling | |
106 | * is not commenced until the threshold has been reached. | |
107 | * This method implements decimation and quantization in order to | |
108 | * be able to provide longer sample traces. | |
109 | * Uses the following global settings: | |
110 | * @param decimation - how much should the signal be decimated. A decimation of N means we keep 1 in N samples, etc. | |
111 | * @param bits_per_sample - bits per sample. Max 8, min 1 bit per sample. | |
112 | * @param averaging If set to true, decimation will use averaging, so that if e.g. decimation is 3, the sample | |
113 | * value that will be used is the average value of the three samples. | |
114 | * @param trigger_threshold - a threshold. The sampling won't commence until this threshold has been reached. Set | |
115 | * to -1 to ignore threshold. | |
116 | * @param silent - is true, now outputs are made. If false, dbprints the status | |
117 | * @return the number of bits occupied by the samples. | |
118 | */ | |
17556415 | 119 | uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averaging, int trigger_threshold, bool silent, int bufsize) { |
1c8fbeb9 | 120 | //bigbuf, to hold the aquired raw data signal |
0644d5e3 | 121 | uint8_t *dest = BigBuf_get_addr(); |
17556415 | 122 | bufsize = (bufsize > 0 && bufsize < BigBuf_max_traceLen()) ? bufsize : BigBuf_max_traceLen(); |
0644d5e3 | 123 | |
c0f15a05 | 124 | //BigBuf_Clear_ext(false); //creates issues with cmdread (marshmellow) |
31abe49f MHS |
125 | |
126 | if(bits_per_sample < 1) bits_per_sample = 1; | |
127 | if(bits_per_sample > 8) bits_per_sample = 8; | |
128 | ||
129 | if(decimation < 1) decimation = 1; | |
130 | ||
131 | // Use a bit stream to handle the output | |
132 | BitstreamOut data = { dest , 0, 0}; | |
133 | int sample_counter = 0; | |
134 | uint8_t sample = 0; | |
135 | //If we want to do averaging | |
136 | uint32_t sample_sum =0 ; | |
137 | uint32_t sample_total_numbers =0 ; | |
138 | uint32_t sample_total_saved =0 ; | |
139 | ||
edaf10af | 140 | while(!BUTTON_PRESS() && !usb_poll_validate_length() ) { |
31abe49f MHS |
141 | WDT_HIT(); |
142 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { | |
5f5b83b7 | 143 | AT91C_BASE_SSC->SSC_THR = 0x43; |
31abe49f MHS |
144 | LED_D_ON(); |
145 | } | |
146 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { | |
147 | sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; | |
148 | LED_D_OFF(); | |
99cf19d9 | 149 | // threshold either high or low values 128 = center 0. if trigger = 178 |
150 | if ((trigger_threshold > 0) && (sample < (trigger_threshold+128)) && (sample > (128-trigger_threshold))) // | |
31abe49f MHS |
151 | continue; |
152 | ||
153 | trigger_threshold = 0; | |
154 | sample_total_numbers++; | |
155 | ||
156 | if(averaging) | |
157 | { | |
158 | sample_sum += sample; | |
159 | } | |
160 | //Check decimation | |
161 | if(decimation > 1) | |
162 | { | |
163 | sample_counter++; | |
164 | if(sample_counter < decimation) continue; | |
165 | sample_counter = 0; | |
166 | } | |
167 | //Averaging | |
168 | if(averaging && decimation > 1) { | |
169 | sample = sample_sum / decimation; | |
170 | sample_sum =0; | |
171 | } | |
172 | //Store the sample | |
173 | sample_total_saved ++; | |
174 | if(bits_per_sample == 8){ | |
175 | dest[sample_total_saved-1] = sample; | |
176 | data.numbits = sample_total_saved << 3;//Get the return value correct | |
177 | if(sample_total_saved >= bufsize) break; | |
178 | } | |
179 | else{ | |
180 | pushBit(&data, sample & 0x80); | |
181 | if(bits_per_sample > 1) pushBit(&data, sample & 0x40); | |
182 | if(bits_per_sample > 2) pushBit(&data, sample & 0x20); | |
183 | if(bits_per_sample > 3) pushBit(&data, sample & 0x10); | |
184 | if(bits_per_sample > 4) pushBit(&data, sample & 0x08); | |
185 | if(bits_per_sample > 5) pushBit(&data, sample & 0x04); | |
186 | if(bits_per_sample > 6) pushBit(&data, sample & 0x02); | |
187 | //Not needed, 8bps is covered above | |
188 | //if(bits_per_sample > 7) pushBit(&data, sample & 0x01); | |
189 | if((data.numbits >> 3) +1 >= bufsize) break; | |
190 | } | |
191 | } | |
192 | } | |
193 | ||
194 | if(!silent) | |
195 | { | |
196 | Dbprintf("Done, saved %d out of %d seen samples at %d bits/sample",sample_total_saved, sample_total_numbers,bits_per_sample); | |
197 | Dbprintf("buffer samples: %02x %02x %02x %02x %02x %02x %02x %02x ...", | |
198 | dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]); | |
199 | } | |
200 | return data.numbits; | |
201 | } | |
202 | /** | |
203 | * @brief Does sample acquisition, ignoring the config values set in the sample_config. | |
204 | * This method is typically used by tag-specific readers who just wants to read the samples | |
205 | * the normal way | |
206 | * @param trigger_threshold | |
207 | * @param silent | |
208 | * @return number of bits sampled | |
209 | */ | |
24c49d36 | 210 | uint32_t DoAcquisition_default(int trigger_threshold, bool silent) { |
17556415 | 211 | return DoAcquisition(1, 8, 0,trigger_threshold, silent, 0); |
31abe49f | 212 | } |
24c49d36 | 213 | uint32_t DoAcquisition_config( bool silent) { |
31abe49f MHS |
214 | return DoAcquisition(config.decimation |
215 | ,config.bits_per_sample | |
216 | ,config.averaging | |
217 | ,config.trigger_threshold | |
17556415 | 218 | ,silent |
219 | ,0); | |
220 | } | |
221 | ||
222 | uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int sample_size) { | |
223 | return DoAcquisition(1, 8, 0, trigger_threshold, silent, sample_size); | |
31abe49f MHS |
224 | } |
225 | ||
24c49d36 | 226 | uint32_t ReadLF(bool activeField, bool silent) { |
227 | if (!silent) | |
228 | printConfig(); | |
31abe49f | 229 | LFSetupFPGAForADC(config.divisor, activeField); |
1fbf8956 | 230 | return DoAcquisition_config(silent); |
31abe49f MHS |
231 | } |
232 | ||
233 | /** | |
234 | * Initializes the FPGA for reader-mode (field on), and acquires the samples. | |
235 | * @return number of bits sampled | |
236 | **/ | |
24c49d36 | 237 | uint32_t SampleLF(bool printCfg) { |
b828a4e1 | 238 | BigBuf_Clear_ext(false); |
239 | uint32_t ret = ReadLF(true, printCfg); | |
240 | FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); | |
241 | return ret; | |
31abe49f MHS |
242 | } |
243 | /** | |
244 | * Initializes the FPGA for snoop-mode (field off), and acquires the samples. | |
245 | * @return number of bits sampled | |
246 | **/ | |
ac2df346 | 247 | uint32_t SnoopLF() { |
b828a4e1 | 248 | BigBuf_Clear_ext(false); |
249 | uint32_t ret = ReadLF(false, true); | |
250 | FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); | |
251 | return ret; | |
31abe49f | 252 | } |
ac2df346 | 253 | |
254 | /** | |
255 | * acquisition of T55x7 LF signal. Similart to other LF, but adjusted with @marshmellows thresholds | |
256 | * the data is collected in BigBuf. | |
257 | **/ | |
94422fa2 | 258 | void doT55x7Acquisition(size_t sample_size) { |
ac2df346 | 259 | |
17556415 | 260 | #define T55xx_READ_UPPER_THRESHOLD 128+60 // 60 grph |
261 | #define T55xx_READ_LOWER_THRESHOLD 128-60 // -60 grph | |
262 | #define T55xx_READ_TOL 5 | |
24c49d36 | 263 | |
ac2df346 | 264 | uint8_t *dest = BigBuf_get_addr(); |
265 | uint16_t bufsize = BigBuf_max_traceLen(); | |
266 | ||
94422fa2 | 267 | if ( bufsize > sample_size ) |
268 | bufsize = sample_size; | |
ac2df346 | 269 | |
24c49d36 | 270 | uint8_t curSample = 0, lastSample = 0; |
271 | uint16_t i = 0, skipCnt = 0; | |
ac2df346 | 272 | bool startFound = false; |
273 | bool highFound = false; | |
6426f6ba | 274 | bool lowFound = false; |
24c49d36 | 275 | |
3f267966 | 276 | while(!BUTTON_PRESS() && !usb_poll_validate_length() && skipCnt < 1000 && (i < bufsize) ) { |
1c8fbeb9 | 277 | WDT_HIT(); |
ac2df346 | 278 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { |
53d5dc64 | 279 | AT91C_BASE_SSC->SSC_THR = 0x43; //43 |
ac2df346 | 280 | LED_D_ON(); |
281 | } | |
282 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { | |
1d0ccbe0 | 283 | curSample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; |
1c8fbeb9 | 284 | LED_D_OFF(); |
285 | ||
94422fa2 | 286 | // skip until the first high sample above threshold |
1d0ccbe0 | 287 | if (!startFound && curSample > T55xx_READ_UPPER_THRESHOLD) { |
6426f6ba | 288 | //if (curSample > lastSample) |
289 | // lastSample = curSample; | |
1d0ccbe0 | 290 | highFound = true; |
ac2df346 | 291 | } else if (!highFound) { |
1d0ccbe0 | 292 | skipCnt++; |
ac2df346 | 293 | continue; |
294 | } | |
53d5dc64 | 295 | // skip until the first low sample below threshold |
6426f6ba | 296 | if (!startFound && curSample < T55xx_READ_LOWER_THRESHOLD) { |
297 | //if (curSample > lastSample) | |
298 | lastSample = curSample; | |
299 | lowFound = true; | |
300 | } else if (!lowFound) { | |
301 | skipCnt++; | |
302 | continue; | |
303 | } | |
304 | ||
94422fa2 | 305 | // skip until first high samples begin to change |
24c49d36 | 306 | if (startFound || curSample > T55xx_READ_LOWER_THRESHOLD + T55xx_READ_TOL){ |
94422fa2 | 307 | // if just found start - recover last sample |
308 | if (!startFound) { | |
6426f6ba | 309 | dest[i++] = lastSample; |
3f267966 | 310 | startFound = true; |
94422fa2 | 311 | } |
312 | // collect samples | |
1d0ccbe0 | 313 | dest[i++] = curSample; |
ac2df346 | 314 | } |
315 | } | |
316 | } | |
1d0ccbe0 | 317 | } |
5f5b83b7 | 318 | /** |
319 | * acquisition of Cotag LF signal. Similart to other LF, since the Cotag has such long datarate RF/384 | |
320 | * and is Manchester?, we directly gather the manchester data into bigbuff | |
321 | **/ | |
322 | ||
323 | #define COTAG_T1 384 | |
324 | #define COTAG_T2 (COTAG_T1>>1) | |
325 | #define COTAG_ONE_THRESHOLD 128+30 | |
326 | #define COTAG_ZERO_THRESHOLD 128-30 | |
17556415 | 327 | #ifndef COTAG_BITS |
328 | #define COTAG_BITS 264 | |
329 | #endif | |
5f5b83b7 | 330 | void doCotagAcquisition(size_t sample_size) { |
331 | ||
332 | uint8_t *dest = BigBuf_get_addr(); | |
333 | uint16_t bufsize = BigBuf_max_traceLen(); | |
334 | ||
335 | if ( bufsize > sample_size ) | |
336 | bufsize = sample_size; | |
337 | ||
338 | dest[0] = 0; | |
339 | uint8_t sample = 0, firsthigh = 0, firstlow = 0; | |
340 | uint16_t i = 0; | |
341 | ||
342 | while (!BUTTON_PRESS() && !usb_poll_validate_length() && (i < bufsize) ) { | |
343 | WDT_HIT(); | |
344 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { | |
345 | AT91C_BASE_SSC->SSC_THR = 0x43; | |
346 | LED_D_ON(); | |
347 | } | |
348 | ||
349 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { | |
350 | sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; | |
351 | LED_D_OFF(); | |
352 | ||
353 | // find first peak | |
354 | if ( !firsthigh ) { | |
355 | if (sample < COTAG_ONE_THRESHOLD) | |
356 | continue; | |
357 | firsthigh = 1; | |
358 | } | |
359 | if ( !firstlow ){ | |
360 | if (sample > COTAG_ZERO_THRESHOLD ) | |
361 | continue; | |
362 | firstlow = 1; | |
363 | } | |
364 | ||
365 | ++i; | |
366 | ||
367 | if ( sample > COTAG_ONE_THRESHOLD) | |
368 | dest[i] = 255; | |
369 | else if ( sample < COTAG_ZERO_THRESHOLD) | |
370 | dest[i] = 0; | |
371 | else | |
372 | dest[i] = dest[i-1]; | |
373 | } | |
374 | } | |
375 | } | |
376 | ||
377 | uint32_t doCotagAcquisitionManchester() { | |
378 | ||
379 | uint8_t *dest = BigBuf_get_addr(); | |
380 | uint16_t bufsize = BigBuf_max_traceLen(); | |
381 | ||
a330987d | 382 | if ( bufsize > COTAG_BITS ) |
383 | bufsize = COTAG_BITS; | |
5f5b83b7 | 384 | |
385 | dest[0] = 0; | |
386 | uint8_t sample = 0, firsthigh = 0, firstlow = 0; | |
387 | uint16_t sample_counter = 0, period = 0; | |
388 | uint8_t curr = 0, prev = 0; | |
389 | ||
390 | while (!BUTTON_PRESS() && !usb_poll_validate_length() && (sample_counter < bufsize) ) { | |
391 | WDT_HIT(); | |
392 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { | |
393 | AT91C_BASE_SSC->SSC_THR = 0x43; | |
394 | LED_D_ON(); | |
395 | } | |
396 | ||
397 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { | |
398 | sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; | |
399 | LED_D_OFF(); | |
400 | ||
401 | // find first peak | |
402 | if ( !firsthigh ) { | |
403 | if (sample < COTAG_ONE_THRESHOLD) | |
404 | continue; | |
405 | firsthigh = 1; | |
406 | } | |
407 | ||
408 | if ( !firstlow ){ | |
409 | if (sample > COTAG_ZERO_THRESHOLD ) | |
410 | continue; | |
411 | firstlow = 1; | |
412 | } | |
413 | ||
414 | // set sample 255, 0, or previous | |
415 | if ( sample > COTAG_ONE_THRESHOLD){ | |
416 | prev = curr; | |
417 | curr = 1; | |
418 | } | |
419 | else if ( sample < COTAG_ZERO_THRESHOLD) { | |
420 | prev = curr; | |
421 | curr = 0; | |
422 | } | |
423 | else { | |
424 | curr = prev; | |
425 | } | |
426 | ||
427 | // full T1 periods, | |
428 | if ( period > 0 ) { | |
429 | --period; | |
430 | continue; | |
431 | } | |
432 | ||
433 | dest[sample_counter] = curr; | |
434 | ++sample_counter; | |
435 | period = COTAG_T1; | |
436 | } | |
437 | } | |
438 | return sample_counter; | |
439 | } |