A simple example.
How does it work?
Oversampling
The core loop of the sampling does only one thing: it continuously looks at the input pin and increments a counter. Only when the input toggles, the counter value is used as an index and the histogram is updated and the counter is reset. By doing so the histogram will contain the run length of observed zeroes or ones, expressed in the time grid of the sampler. For a 1MHz bit stream the LPC 1768 should be capable to over sample approximately four times.
Grouping of run length
A filled histogram of run lengths, of both the zero and one symbols, will contain groups of adjacent run lengths values separated by empty spaces. If the sigma delta is connected to an analog voltage at exactly 25% of the range, the output pattern of the bit stream, expressed in the time grid of the ADC, will be close to 000100001000100001000100001... With approximately four times oversampling the LPC board may capture a data stream like: 0000, or expressed in run lengths: 10, 4, 16, 3, 12, 3, 15, 3, 11, 3, 16, 4. The histogram of zeroes will be filled with 1 at positions 10, 11, 12, 15 and 16, while the histogram of ones will be filled with 4 and 2 respectively at position 3 and 4.
Assign values to groups
After captured the data, the histogram will be scanned for groups of adjacent run lengths. A begin and end pointer/index of each will be stored in object type "Recovered". Once the whole histogram is scanned, a list of run length groups is determined. For each groups the average value of the run length will be determined.
Calculate Over Sample Ratio and Offset
The minimum distance between two average values will be a reasonable accurate value of the over sample factor. In our example the group of symbols consists of ADC run lengths of:
- one: occurs 4 times with length 3 and 2 times 4, thus the average is 3.333.
- three: consists of 11, 12 and 13 and thus an average of 12.0.
- four: consists of one time 15 and two times 16: average equals 15.666.
The average distance between one and three is now 8.666. Therefore the average distance between three and four, only 3.666, a reasonable approximation of the over sample ratio. When acquiring more data, the average values will approximate the oversampling ratio better. An alternative method would be two take the shortest symbol as a value of the oversample factor, as this is the unit. However, as the loop requires some pre-processing before actively it can start counting, the average run length of the symbol with run length one will always be to lower than the actual over sample ratio. This creates an offset in the correlation of bit stream symbol to over sample data..
Known limitations
- The amount of samples is only approximated, or more accurate, taken as a minimum value. As only the counter is compared once a complete run length of the same symbols is seen, it will be always slightly above the require value.
- The amount of samples taken is hard coded. No means to vary this while running the application.
- When the ADC input is very close or the maximum input voltage (or very close tot the minimum input voltage) the resulting bit stream will contain mostly very long run length of one's and hardly any zero (or vice versa). As no clock is connected, the stream may become out of synchronization for these cases.
- Only the DC level is calculated, as a sum of all ones divided by the amount of symbols. Technically one could add Fourier transform in the post-processing and calculate SNR, THD, SINAD, ENOB etc, This requires another data structure of the histogram: store run length in the sequence they appear.
- The algorithm works only correct given two assumptions. There should be exactly one group of empty spaces between two groups of captured run lengths (each representing a different bit stream run length). And each group of run lengths may not contain any empty position. Another decoder http://en.wikipedia.org/wiki/Viterbi_algorithm would possibly do better and even could estimate a qualification number.
Revision 13:2a66d067310b, committed 2015-06-18
- Comitter:
- pscholtens
- Date:
- Thu Jun 18 12:31:06 2015 +0000
- Parent:
- 12:75acace69521
- Commit message:
- Re-written core loop with use of macro's. Fixed number of un-synchronized number of samples.
Changed in this revision
main.cpp | Show annotated file Show diff for this revision Revisions of this file |
mbed.bld | Show annotated file Show diff for this revision Revisions of this file |
diff -r 75acace69521 -r 2a66d067310b main.cpp --- a/main.cpp Wed Jun 17 08:23:26 2015 +0000 +++ b/main.cpp Thu Jun 18 12:31:06 2015 +0000 @@ -1,6 +1,15 @@ #include "mbed.h" #include "FastIO.h" +/* version 0.1.5, P.C.S. Scholtens, Datang NXP, June 18th 2015, Nijmegen, Netherlands + - Converter function parameter num_unsync_samples in fill_histogram() to constant + NUM_UNSYNC_SAMPLES to speed up comparison of the core while loop. + - Re-written core loop with nested macro's. This ensures that the code will stay + exactly the same for both symbol 0 and 1 (Don't Repeat Yourself...). + - At start-up mention is this executable has the debug and/or allow option activated. + - Print also the assigned value of the detected underflow sequences. +*/ + /* version 0.1.4, P.C.S. Scholtens, Datang NXP, June 17th 2015, Nijmegen, Netherlands - Flattened core loop of the fill_histogram() function. As both symbols 0 and 1 have their own core, sampling can be done faster: there's no comparison with the @@ -87,10 +96,11 @@ */ -#define DEPTH 1024 -#define WATCH_DOG_TIME 10 +#define DEPTH 1024 +#define WATCH_DOG_TIME 10 +#define NUM_UNSYNC_SAMPLES 4e7 #undef DEBUG_MODE -#undef ALLOW_OUT_OF_RANGE +//#define ALLOW_OUT_OF_RANGE /* Reserve memory space for the histogram */ unsigned int zeros[DEPTH]; @@ -165,7 +175,7 @@ void print_histogram() { pc.printf(" Sequence Zeros Ones Assign\n"); if ( zeros[0]+ones[0] != 0 ) { - pc.printf("Underflow %8i %8i\n",zeros[0],ones[0]); + pc.printf("Underflow %8i %8i %8i\n",zeros[0],ones[0],assign[0]); } for (unsigned int i = 1; i < DEPTH-1; i++) { if ( zeros[i]+ones[i] != 0 ) { @@ -185,57 +195,37 @@ } /* Function which fill the histogram */ -void fill_histogram(unsigned int num_unsync_samples) { +void fill_histogram() { + +/* Make the macro definition of clip_run_length() macro dependent of the existence of ALLOW_OUT_OF_RANGE. + * This optional macro tests if run length exceeds the defined depth of histogram, and if so assigns the clip value. */ +#ifdef ALLOW_OUT_OF_RANGE +#define clip_run_length() ; +#else +#define clip_run_length() if (run_length > DEPTH-1) {run_length = DEPTH-1; } +#endif +/* Now define the center loop macro as it will be used in two symbol flavours: either 0 or 1. */ +#define fast_loop(symbol) run_length = 0; while( (bool) bitstream == symbol) {run_length++;}; count += run_length; clip_run_length(); unsigned int count = 0; unsigned int run_length = 0; /* Switch on watch dog timer */ timeout.attach(&at_time_out, WATCH_DOG_TIME); /* Implements run-in: skip the first sequence of ZEROs as it is only a partial one. */ - while(!(bool) bitstream) { - /* Do nothing, intentionally */; - }; + fast_loop(0); /* Implements run-in: skip the first sequence of ONEs as we always want to start with zeros. */ - while( (bool) bitstream) { - /* Do nothing, intentionally */; - }; - run_length = 0; - /* Start actual counting ZEROs here, store in variable run_length (will be clipped to DEPTH) */ - while(count < num_unsync_samples) { - /* Core of the ZERO loop */ - while(! (bool) bitstream) { - run_length++; - }; - /* Increment counter before clipping to preserve accuracy. */ - count += run_length; - /* Test if run length exceeds depth of histogram, if so assign clip value. */ -#ifndef ALLOW_OUT_OF_RANGE - if (run_length > DEPTH-1) { - run_length = DEPTH-1; - } -#endif - /* Now write in histogram array of ZERO's */ + fast_loop(1); + while(count < NUM_UNSYNC_SAMPLES ) { + /* Core of the loop! */ + fast_loop(0); zeros[run_length]++; - /* Reset for next run length counting loop */ - run_length = 0; - /* Core of the ONES loop */ - while( (bool) bitstream) { - run_length++; - }; - /* Increment counter before clipping to preserve accuracy. */ - count += run_length; - /* Test if run length exceeds depth of histogram, if so assign clip value. */ -#ifndef ALLOW_OUT_OF_RANGE - if (run_length > DEPTH-1) { - run_length = DEPTH-1; - } -#endif - /* Now write in histogram array of ONES's */ + fast_loop(1); ones[run_length]++; - /* Reset for next run length counting loop */ - run_length = 0; } /* Switch off watch dog timer */ timeout.detach(); +/* Do not use outside this scope */ +#undef fast_loop +#undef clip_run_length } /* Here we count the number of unsynchronized symbols, mimicing previous implementation */ @@ -398,8 +388,16 @@ float synced_dutycycle_new, synced_voltage_new; pc.baud(115200); - pc.printf("Bitstream counter, version 0.1.4, P.C.S. Scholtens, June 17th 2015, Nijmegen, Netherlands.\n"); - pc.printf("Build " __DATE__ " " __TIME__ "\n"); + pc.printf("Bitstream counter, version 0.1.5, P.C.S. Scholtens, June 18th 2015, Nijmegen, Netherlands.\n"); + pc.printf("Build: " __DATE__ ", " __TIME__ ); +#ifdef DEBUG_MODE + pc.printf(", debug mode"); +#endif +#ifdef ALLOW_OUT_OF_RANGE + pc.printf(", allows out-of-range"); +#endif + pc.printf(".\n"); + /*LPC_TIM2->PR = 0x0000002F; / * decimal 47 */ /*LPC_TIM3->PR = 24;*/ while(1) { @@ -407,7 +405,7 @@ myled = 1; clear_histogram(); timer.start(); - fill_histogram(4e7); + fill_histogram(); timer.stop(); #ifdef DEBUG_MODE num_of_zeros = get_num_unsync_symbols(0);
diff -r 75acace69521 -r 2a66d067310b mbed.bld --- a/mbed.bld Wed Jun 17 08:23:26 2015 +0000 +++ b/mbed.bld Thu Jun 18 12:31:06 2015 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/mbed_official/code/mbed/builds/433970e64889 \ No newline at end of file +http://mbed.org/users/mbed_official/code/mbed/builds/7cff1c4259d7 \ No newline at end of file