A simple example.

Dependencies:   mbed FastIO

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.
Committer:
pscholtens
Date:
Fri Apr 17 02:19:41 2015 +0000
Revision:
5:1c0bfd69719f
Parent:
4:27a2eaee71ac
Child:
6:a5fc4e2ff34b
Version 0.0.8 Finally it works correctly, let's start measuring accuractely!

Who changed what in which revision?

UserRevisionLine numberNew contents of line
pscholtens 0:dc1b041f713e 1 #include "mbed.h"
pscholtens 0:dc1b041f713e 2
pscholtens 5:1c0bfd69719f 3 /* version 0.0.8, P.C.S. Scholtens, Datang NXP, April 17th 2015, Shanghai, PR China
pscholtens 5:1c0bfd69719f 4 - Corrected assigned synchronized values, as the first appearance wasn't assigned.
pscholtens 5:1c0bfd69719f 5 */
pscholtens 5:1c0bfd69719f 6
pscholtens 4:27a2eaee71ac 7 /* version 0.0.7, P.C.S. Scholtens, Datang NXP, April 16/17th 2015, Shanghai, PR China
pscholtens 4:27a2eaee71ac 8 - Method written to assign synchronized values to run-length.
pscholtens 4:27a2eaee71ac 9 - Added warnings for underflow.
pscholtens 4:27a2eaee71ac 10 - After skipped run-in cycles, copy the current bit, to prevent false single hit.
pscholtens 4:27a2eaee71ac 11 */
pscholtens 4:27a2eaee71ac 12
pscholtens 4:27a2eaee71ac 13 /* version 0.0.6, P.C.S. Scholtens, Datang NXP, April 15th, 2015, Shanghai, PR China
pscholtens 3:8d13bf073e92 14 - Corrected duty-cycle output for actual value of symbols (Thanks to Richard Zhu!).
pscholtens 3:8d13bf073e92 15 - Skipped run-in cycles to avoid pollution of the histogram with the first, most
pscholtens 3:8d13bf073e92 16 likely partial, sequence captured.
pscholtens 3:8d13bf073e92 17 - Added warnings for overflow.
pscholtens 3:8d13bf073e92 18 */
pscholtens 3:8d13bf073e92 19
pscholtens 2:5e37831540c7 20 /* version 0.0.5, P.C.S. Scholtens, Datang NXP, April 14th, 2015, Shanghai, PR China
pscholtens 3:8d13bf073e92 21 Implement histogram to find run lengths of zeroes and ones. */
pscholtens 2:5e37831540c7 22
pscholtens 1:2551859fbc25 23 /* version 0.0.4, P.C.S. Scholtens, Datang NXP, April 14th, 2015, Shanghai, PR China
pscholtens 1:2551859fbc25 24 Implement histogram to find run lengths of zroes and ones. */
pscholtens 1:2551859fbc25 25
pscholtens 1:2551859fbc25 26 /* version 0.0.3, P.C.S. Scholtens, Datang NXP, April 14th, 2015, Shanghai, PR China
pscholtens 1:2551859fbc25 27 Initial version. No synchronixzation of the symbols is done. */
pscholtens 0:dc1b041f713e 28
pscholtens 0:dc1b041f713e 29 /* See also:
pscholtens 0:dc1b041f713e 30 https://developer.mbed.org/forum/bugs-suggestions/topic/3464/
pscholtens 0:dc1b041f713e 31 */
pscholtens 0:dc1b041f713e 32
pscholtens 1:2551859fbc25 33 #define DEPTH 128
pscholtens 1:2551859fbc25 34
pscholtens 1:2551859fbc25 35 /* Reserve memory space for the histogram */
pscholtens 1:2551859fbc25 36 unsigned int zeros[DEPTH];
pscholtens 1:2551859fbc25 37 unsigned int ones[DEPTH];
pscholtens 4:27a2eaee71ac 38 unsigned int assign[DEPTH];
pscholtens 1:2551859fbc25 39
pscholtens 1:2551859fbc25 40 DigitalIn bitstream(p11);
pscholtens 0:dc1b041f713e 41 DigitalOut myled(LED1);
pscholtens 0:dc1b041f713e 42 Serial pc(USBTX, USBRX); // tx, rx
pscholtens 0:dc1b041f713e 43
pscholtens 0:dc1b041f713e 44 Timer t;
pscholtens 0:dc1b041f713e 45
pscholtens 1:2551859fbc25 46 /* A function to clear the contents of both histograms */
pscholtens 1:2551859fbc25 47 void clear_histogram() {
pscholtens 1:2551859fbc25 48 for(unsigned int i = 0; i < DEPTH; i++) {
pscholtens 1:2551859fbc25 49 zeros[i] = 0;
pscholtens 1:2551859fbc25 50 ones[i] = 0;
pscholtens 1:2551859fbc25 51 }
pscholtens 1:2551859fbc25 52 }
pscholtens 1:2551859fbc25 53
pscholtens 1:2551859fbc25 54 /* Print the contents of the histogram, excluding the empty values */
pscholtens 1:2551859fbc25 55 void print_histogram() {
pscholtens 4:27a2eaee71ac 56 pc.printf(" Sequence Zeros Ones Assign\n");
pscholtens 4:27a2eaee71ac 57 if ( zeros[0]+ones[0] != 0 ) {
pscholtens 4:27a2eaee71ac 58 pc.printf("Underflow %8i %8i\n",zeros[0],ones[0]);
pscholtens 4:27a2eaee71ac 59 }
pscholtens 4:27a2eaee71ac 60 for (unsigned int i = 1; i < DEPTH-1; i++) {
pscholtens 1:2551859fbc25 61 if ( zeros[i]+ones[i] != 0 ) {
pscholtens 4:27a2eaee71ac 62 pc.printf(" %8i %8i %8i %8i\n",i,zeros[i],ones[i],assign[i]);
pscholtens 1:2551859fbc25 63 }
pscholtens 1:2551859fbc25 64 }
pscholtens 3:8d13bf073e92 65 if ( zeros[DEPTH-1]+ones[DEPTH-1] != 0 ) {
pscholtens 4:27a2eaee71ac 66 pc.printf("Overflow %8i %8i\n",zeros[DEPTH-1],ones[DEPTH-1]);
pscholtens 3:8d13bf073e92 67 }
pscholtens 3:8d13bf073e92 68
pscholtens 1:2551859fbc25 69 }
pscholtens 1:2551859fbc25 70
pscholtens 1:2551859fbc25 71 /* Function which fill the histogram */
pscholtens 1:2551859fbc25 72 void fill_histogram(unsigned int num_unsync_samples) {
pscholtens 1:2551859fbc25 73 unsigned int count = 0;
pscholtens 1:2551859fbc25 74 unsigned int run_length = 0;
pscholtens 2:5e37831540c7 75 bool previous_bit = (bool) bitstream;
pscholtens 3:8d13bf073e92 76 /* Implements run-in: skip the first sequence as it is only a partial one. */
pscholtens 3:8d13bf073e92 77 while((previous_bit == (bool) bitstream) && (run_length < DEPTH-1)) {
pscholtens 3:8d13bf073e92 78 run_length++;
pscholtens 3:8d13bf073e92 79 };
pscholtens 4:27a2eaee71ac 80 previous_bit = !previous_bit;
pscholtens 3:8d13bf073e92 81 /* Start actual counting here */
pscholtens 3:8d13bf073e92 82 run_length = 0;
pscholtens 1:2551859fbc25 83 while(count < num_unsync_samples) {
pscholtens 2:5e37831540c7 84 while((previous_bit == (bool) bitstream) && (run_length < DEPTH-1)) {
pscholtens 1:2551859fbc25 85 run_length++;
pscholtens 1:2551859fbc25 86 };
pscholtens 1:2551859fbc25 87 if (previous_bit) {
pscholtens 1:2551859fbc25 88 ones[run_length]++;
pscholtens 1:2551859fbc25 89 }
pscholtens 1:2551859fbc25 90 else {
pscholtens 1:2551859fbc25 91 zeros[run_length]++;
pscholtens 1:2551859fbc25 92 }
pscholtens 2:5e37831540c7 93 count += run_length;
pscholtens 2:5e37831540c7 94 run_length = 0;
pscholtens 2:5e37831540c7 95 previous_bit = !previous_bit;
pscholtens 1:2551859fbc25 96 }
pscholtens 1:2551859fbc25 97 }
pscholtens 1:2551859fbc25 98
pscholtens 1:2551859fbc25 99 /* Here we count the number of unsynchronized symbols, mimicing previous implementation */
pscholtens 1:2551859fbc25 100 unsigned int get_num_unsync_symbols(int symbol) {
pscholtens 1:2551859fbc25 101 unsigned int sum = 0;
pscholtens 1:2551859fbc25 102 for (unsigned int i = 0; i < DEPTH; i++) {
pscholtens 1:2551859fbc25 103 if (symbol == 0) {
pscholtens 1:2551859fbc25 104 sum += zeros[i];
pscholtens 1:2551859fbc25 105 } else {
pscholtens 1:2551859fbc25 106 sum += ones[i];
pscholtens 1:2551859fbc25 107 }
pscholtens 1:2551859fbc25 108 }
pscholtens 1:2551859fbc25 109 return sum;
pscholtens 1:2551859fbc25 110 }
pscholtens 1:2551859fbc25 111
pscholtens 3:8d13bf073e92 112 /* Calculate the value, using the unsynchronized method */
pscholtens 3:8d13bf073e92 113 unsigned int get_value_unsync_symbols(int symbol) {
pscholtens 3:8d13bf073e92 114 unsigned int sum = 0;
pscholtens 3:8d13bf073e92 115 for (unsigned int i = 0; i < DEPTH; i++) {
pscholtens 3:8d13bf073e92 116 if (symbol == 0) {
pscholtens 3:8d13bf073e92 117 sum += i*zeros[i];
pscholtens 3:8d13bf073e92 118 } else {
pscholtens 3:8d13bf073e92 119 sum += i*ones[i];
pscholtens 3:8d13bf073e92 120 }
pscholtens 3:8d13bf073e92 121 }
pscholtens 3:8d13bf073e92 122 return sum;
pscholtens 3:8d13bf073e92 123 }
pscholtens 3:8d13bf073e92 124
pscholtens 4:27a2eaee71ac 125 /* Calculate the value, using the synchronization algorithm */
pscholtens 4:27a2eaee71ac 126 unsigned int get_value_synced_symbols(int symbol) {
pscholtens 4:27a2eaee71ac 127 bool presence = false;
pscholtens 4:27a2eaee71ac 128 int value = 0;
pscholtens 4:27a2eaee71ac 129 for (unsigned int i = 0; i < DEPTH; i++) {
pscholtens 4:27a2eaee71ac 130 if ( zeros[i]+ones[i] != 0 ) {
pscholtens 4:27a2eaee71ac 131 if (presence) {
pscholtens 5:1c0bfd69719f 132 assign[i] = value;
pscholtens 4:27a2eaee71ac 133 } else {
pscholtens 4:27a2eaee71ac 134 value++;
pscholtens 5:1c0bfd69719f 135 presence = true;
pscholtens 5:1c0bfd69719f 136 assign[i] = value;
pscholtens 4:27a2eaee71ac 137 }
pscholtens 4:27a2eaee71ac 138 } else {
pscholtens 4:27a2eaee71ac 139 presence = false;
pscholtens 4:27a2eaee71ac 140 }
pscholtens 4:27a2eaee71ac 141 }
pscholtens 4:27a2eaee71ac 142 /* Now do the actual summation of symbol values */
pscholtens 4:27a2eaee71ac 143 unsigned int sum = 0;
pscholtens 4:27a2eaee71ac 144 for (unsigned int i = 0; i < DEPTH; i++) {
pscholtens 4:27a2eaee71ac 145 if (symbol == 0) {
pscholtens 4:27a2eaee71ac 146 sum += assign[i]*zeros[i];
pscholtens 4:27a2eaee71ac 147 } else {
pscholtens 4:27a2eaee71ac 148 sum += assign[i]*ones[i];
pscholtens 4:27a2eaee71ac 149 }
pscholtens 4:27a2eaee71ac 150 }
pscholtens 4:27a2eaee71ac 151 return sum;
pscholtens 4:27a2eaee71ac 152 }
pscholtens 4:27a2eaee71ac 153
pscholtens 4:27a2eaee71ac 154
pscholtens 1:2551859fbc25 155 /* The main routine of the program */
pscholtens 1:2551859fbc25 156
pscholtens 0:dc1b041f713e 157 int main() {
pscholtens 4:27a2eaee71ac 158 unsigned int num_of_zeros, num_of_ones, value_of_unsync_zeros, value_of_unsync_ones, value_of_synced_zeros, value_of_synced_ones;
pscholtens 3:8d13bf073e92 159 float unsync_dutycycle, synced_dutycycle, unsync_voltage, synced_voltage;
pscholtens 1:2551859fbc25 160 pc.baud(115200);
pscholtens 5:1c0bfd69719f 161 pc.printf("Bitstream counter, version 0.0.8, P.C.S. Scholtens, April 17th 2015, Shanghai, PR China.\n");
pscholtens 0:dc1b041f713e 162 /*LPC_TIM2->PR = 0x0000002F; / * decimal 47 */
pscholtens 0:dc1b041f713e 163 /*LPC_TIM3->PR = 24;*/
pscholtens 1:2551859fbc25 164 clear_histogram();
pscholtens 0:dc1b041f713e 165 while(1) {
pscholtens 0:dc1b041f713e 166 t.reset();
pscholtens 0:dc1b041f713e 167 myled = 1;
pscholtens 1:2551859fbc25 168 clear_histogram();
pscholtens 4:27a2eaee71ac 169 t.start();
pscholtens 5:1c0bfd69719f 170 fill_histogram(1e7);
pscholtens 4:27a2eaee71ac 171 t.stop();
pscholtens 4:27a2eaee71ac 172 pc.printf("\n------ Captured Histogram ------\n");
pscholtens 1:2551859fbc25 173 print_histogram();
pscholtens 1:2551859fbc25 174 num_of_zeros = get_num_unsync_symbols(0);
pscholtens 1:2551859fbc25 175 num_of_ones = get_num_unsync_symbols(1);
pscholtens 3:8d13bf073e92 176 value_of_unsync_zeros = get_value_unsync_symbols(0);
pscholtens 3:8d13bf073e92 177 value_of_unsync_ones = get_value_unsync_symbols(1);
pscholtens 3:8d13bf073e92 178 unsync_dutycycle = ((float) value_of_unsync_ones)/(value_of_unsync_zeros+value_of_unsync_ones); /* We need to typecast one of the integers to float, otherwise the result is rounded till zero. */
pscholtens 3:8d13bf073e92 179 unsync_voltage = (0.5*13*unsync_dutycycle+1)*0.9; /* This is the ADC formula, see analysisSigmaDeltaADC.pdf */
pscholtens 4:27a2eaee71ac 180 value_of_synced_zeros = get_value_synced_symbols(0);
pscholtens 4:27a2eaee71ac 181 value_of_synced_ones = get_value_synced_symbols(1);
pscholtens 4:27a2eaee71ac 182 synced_dutycycle = ((float) value_of_synced_ones)/(value_of_synced_zeros+value_of_synced_ones); /* We need to typecast one of the integers to float, otherwise the result is rounded till zero. */
pscholtens 4:27a2eaee71ac 183 synced_voltage = (0.5*13*synced_dutycycle+1)*0.9; /* This is the ADC formula, see analysisSigmaDeltaADC.pdf */
pscholtens 4:27a2eaee71ac 184 pc.printf("------ Unsynchronized Results ------\n");
pscholtens 4:27a2eaee71ac 185 pc.printf("Counted Sequences %8i %8i\n", num_of_zeros , num_of_ones);
pscholtens 4:27a2eaee71ac 186 pc.printf("Summed Values %8i %8i\n", value_of_unsync_zeros, value_of_unsync_ones);
pscholtens 4:27a2eaee71ac 187 pc.printf("Duty Cycle %f, equals %f Volt\n", unsync_dutycycle , unsync_voltage);
pscholtens 4:27a2eaee71ac 188 pc.printf("------ Synchronized Results ------\n");
pscholtens 4:27a2eaee71ac 189 pc.printf("Summed Values %8i %8i\n", value_of_synced_zeros, value_of_synced_ones);
pscholtens 4:27a2eaee71ac 190 pc.printf("Duty Cyle %f, equals %f Volt\n", synced_dutycycle , synced_voltage);
pscholtens 4:27a2eaee71ac 191 pc.printf("------------------------------------\n");
pscholtens 4:27a2eaee71ac 192 pc.printf("Measured in %f sec.\n", t.read());
pscholtens 4:27a2eaee71ac 193 pc.printf("====================================\n");
pscholtens 0:dc1b041f713e 194 myled = 0;
pscholtens 5:1c0bfd69719f 195 wait(0.1);
pscholtens 0:dc1b041f713e 196 }
pscholtens 4:27a2eaee71ac 197 }