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:
Tue Apr 21 12:19:41 2015 +0000
Revision:
6:a5fc4e2ff34b
Parent:
5:1c0bfd69719f
Child:
7:5141bd76b08d
version 0.0.9: Speedier performance dus to simpler core loop. Overflow will not toggle symbol anymore. Update of mbed library.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
pscholtens 0:dc1b041f713e 1 #include "mbed.h"
pscholtens 0:dc1b041f713e 2
pscholtens 6:a5fc4e2ff34b 3 /* version 0.0.9, P.C.S. Scholtens, Datang NXP, April 21th 2015, Nijmegen, Netherlands
pscholtens 6:a5fc4e2ff34b 4 - Run time counter overflow fill now continue looking for same bit, however
pscholtens 6:a5fc4e2ff34b 5 clipping the actual store value. This prevents underflow occurence of other symbol
pscholtens 6:a5fc4e2ff34b 6 and may create lock if no bitstream is present.
pscholtens 6:a5fc4e2ff34b 7 - Time out function added to prevent lock in case no bitstream is present.
pscholtens 6:a5fc4e2ff34b 8 - Timer object renamed for clarity from t to timer, see http://xkcd.org/1513/
pscholtens 6:a5fc4e2ff34b 9 - Includes updated build of library mbed.
pscholtens 6:a5fc4e2ff34b 10 - Out-of-range of run length moved outside core loop, to speed up bitstream sampling
pscholtens 6:a5fc4e2ff34b 11 and consequently improving accuracy.
pscholtens 6:a5fc4e2ff34b 12 */
pscholtens 6:a5fc4e2ff34b 13
pscholtens 5:1c0bfd69719f 14 /* version 0.0.8, P.C.S. Scholtens, Datang NXP, April 17th 2015, Shanghai, PR China
pscholtens 5:1c0bfd69719f 15 - Corrected assigned synchronized values, as the first appearance wasn't assigned.
pscholtens 5:1c0bfd69719f 16 */
pscholtens 5:1c0bfd69719f 17
pscholtens 4:27a2eaee71ac 18 /* version 0.0.7, P.C.S. Scholtens, Datang NXP, April 16/17th 2015, Shanghai, PR China
pscholtens 4:27a2eaee71ac 19 - Method written to assign synchronized values to run-length.
pscholtens 4:27a2eaee71ac 20 - Added warnings for underflow.
pscholtens 4:27a2eaee71ac 21 - After skipped run-in cycles, copy the current bit, to prevent false single hit.
pscholtens 4:27a2eaee71ac 22 */
pscholtens 4:27a2eaee71ac 23
pscholtens 4:27a2eaee71ac 24 /* version 0.0.6, P.C.S. Scholtens, Datang NXP, April 15th, 2015, Shanghai, PR China
pscholtens 3:8d13bf073e92 25 - Corrected duty-cycle output for actual value of symbols (Thanks to Richard Zhu!).
pscholtens 3:8d13bf073e92 26 - Skipped run-in cycles to avoid pollution of the histogram with the first, most
pscholtens 3:8d13bf073e92 27 likely partial, sequence captured.
pscholtens 3:8d13bf073e92 28 - Added warnings for overflow.
pscholtens 3:8d13bf073e92 29 */
pscholtens 3:8d13bf073e92 30
pscholtens 2:5e37831540c7 31 /* version 0.0.5, P.C.S. Scholtens, Datang NXP, April 14th, 2015, Shanghai, PR China
pscholtens 3:8d13bf073e92 32 Implement histogram to find run lengths of zeroes and ones. */
pscholtens 2:5e37831540c7 33
pscholtens 1:2551859fbc25 34 /* version 0.0.4, P.C.S. Scholtens, Datang NXP, April 14th, 2015, Shanghai, PR China
pscholtens 1:2551859fbc25 35 Implement histogram to find run lengths of zroes and ones. */
pscholtens 1:2551859fbc25 36
pscholtens 1:2551859fbc25 37 /* version 0.0.3, P.C.S. Scholtens, Datang NXP, April 14th, 2015, Shanghai, PR China
pscholtens 6:a5fc4e2ff34b 38 Initial version. No synchronization of the symbols is done. */
pscholtens 0:dc1b041f713e 39
pscholtens 0:dc1b041f713e 40 /* See also:
pscholtens 0:dc1b041f713e 41 https://developer.mbed.org/forum/bugs-suggestions/topic/3464/
pscholtens 0:dc1b041f713e 42 */
pscholtens 0:dc1b041f713e 43
pscholtens 1:2551859fbc25 44 #define DEPTH 128
pscholtens 6:a5fc4e2ff34b 45 #define WATCH_DOG_TIME 4
pscholtens 1:2551859fbc25 46
pscholtens 1:2551859fbc25 47 /* Reserve memory space for the histogram */
pscholtens 1:2551859fbc25 48 unsigned int zeros[DEPTH];
pscholtens 1:2551859fbc25 49 unsigned int ones[DEPTH];
pscholtens 4:27a2eaee71ac 50 unsigned int assign[DEPTH];
pscholtens 1:2551859fbc25 51
pscholtens 6:a5fc4e2ff34b 52 DigitalIn bitstream(p11);
pscholtens 0:dc1b041f713e 53 DigitalOut myled(LED1);
pscholtens 6:a5fc4e2ff34b 54 Serial pc(USBTX, USBRX); // tx, rx
pscholtens 6:a5fc4e2ff34b 55 Timer timer;
pscholtens 6:a5fc4e2ff34b 56 Timeout timeout;
pscholtens 0:dc1b041f713e 57
pscholtens 1:2551859fbc25 58 /* A function to clear the contents of both histograms */
pscholtens 1:2551859fbc25 59 void clear_histogram() {
pscholtens 1:2551859fbc25 60 for(unsigned int i = 0; i < DEPTH; i++) {
pscholtens 1:2551859fbc25 61 zeros[i] = 0;
pscholtens 1:2551859fbc25 62 ones[i] = 0;
pscholtens 1:2551859fbc25 63 }
pscholtens 1:2551859fbc25 64 }
pscholtens 1:2551859fbc25 65
pscholtens 1:2551859fbc25 66 /* Print the contents of the histogram, excluding the empty values */
pscholtens 1:2551859fbc25 67 void print_histogram() {
pscholtens 4:27a2eaee71ac 68 pc.printf(" Sequence Zeros Ones Assign\n");
pscholtens 4:27a2eaee71ac 69 if ( zeros[0]+ones[0] != 0 ) {
pscholtens 4:27a2eaee71ac 70 pc.printf("Underflow %8i %8i\n",zeros[0],ones[0]);
pscholtens 4:27a2eaee71ac 71 }
pscholtens 4:27a2eaee71ac 72 for (unsigned int i = 1; i < DEPTH-1; i++) {
pscholtens 1:2551859fbc25 73 if ( zeros[i]+ones[i] != 0 ) {
pscholtens 4:27a2eaee71ac 74 pc.printf(" %8i %8i %8i %8i\n",i,zeros[i],ones[i],assign[i]);
pscholtens 1:2551859fbc25 75 }
pscholtens 1:2551859fbc25 76 }
pscholtens 3:8d13bf073e92 77 if ( zeros[DEPTH-1]+ones[DEPTH-1] != 0 ) {
pscholtens 4:27a2eaee71ac 78 pc.printf("Overflow %8i %8i\n",zeros[DEPTH-1],ones[DEPTH-1]);
pscholtens 3:8d13bf073e92 79 }
pscholtens 3:8d13bf073e92 80
pscholtens 1:2551859fbc25 81 }
pscholtens 1:2551859fbc25 82
pscholtens 6:a5fc4e2ff34b 83 /* Will only be called if measurement time exceeds preset watch dog timer. */
pscholtens 6:a5fc4e2ff34b 84 void at_time_out() {
pscholtens 6:a5fc4e2ff34b 85 pc.printf("Input clipped to level %i, no bitstream present.\n", (int) bitstream);
pscholtens 6:a5fc4e2ff34b 86 timeout.attach(&at_time_out, WATCH_DOG_TIME);
pscholtens 6:a5fc4e2ff34b 87 }
pscholtens 6:a5fc4e2ff34b 88
pscholtens 1:2551859fbc25 89 /* Function which fill the histogram */
pscholtens 1:2551859fbc25 90 void fill_histogram(unsigned int num_unsync_samples) {
pscholtens 1:2551859fbc25 91 unsigned int count = 0;
pscholtens 1:2551859fbc25 92 unsigned int run_length = 0;
pscholtens 2:5e37831540c7 93 bool previous_bit = (bool) bitstream;
pscholtens 6:a5fc4e2ff34b 94 /* Switch on watch dog timer */
pscholtens 6:a5fc4e2ff34b 95 timeout.attach(&at_time_out, WATCH_DOG_TIME);
pscholtens 3:8d13bf073e92 96 /* Implements run-in: skip the first sequence as it is only a partial one. */
pscholtens 6:a5fc4e2ff34b 97 while(previous_bit == (bool) bitstream) {
pscholtens 6:a5fc4e2ff34b 98 /* Do nothing, intentionally */;
pscholtens 3:8d13bf073e92 99 };
pscholtens 4:27a2eaee71ac 100 previous_bit = !previous_bit;
pscholtens 3:8d13bf073e92 101 run_length = 0;
pscholtens 6:a5fc4e2ff34b 102 /* Start actual counting here, store in variable run_length (will be clipped to DEPTH) */
pscholtens 1:2551859fbc25 103 while(count < num_unsync_samples) {
pscholtens 6:a5fc4e2ff34b 104 /* Core of the loop */
pscholtens 6:a5fc4e2ff34b 105 while(previous_bit == (bool) bitstream) {
pscholtens 1:2551859fbc25 106 run_length++;
pscholtens 1:2551859fbc25 107 };
pscholtens 6:a5fc4e2ff34b 108 /* Increment counter before clipping to preserve accuracy. */
pscholtens 6:a5fc4e2ff34b 109 count += run_length;
pscholtens 6:a5fc4e2ff34b 110 /* Test if run length exceeds depth of histogram, if so assign clip value. */
pscholtens 6:a5fc4e2ff34b 111 if (run_length < DEPTH-1) {
pscholtens 6:a5fc4e2ff34b 112 run_length = DEPTH-1;
pscholtens 6:a5fc4e2ff34b 113 }
pscholtens 6:a5fc4e2ff34b 114 /* Now write in histogram array of interest */
pscholtens 1:2551859fbc25 115 if (previous_bit) {
pscholtens 1:2551859fbc25 116 ones[run_length]++;
pscholtens 1:2551859fbc25 117 }
pscholtens 1:2551859fbc25 118 else {
pscholtens 1:2551859fbc25 119 zeros[run_length]++;
pscholtens 1:2551859fbc25 120 }
pscholtens 6:a5fc4e2ff34b 121 /* Reset for next run length counting loop */
pscholtens 2:5e37831540c7 122 run_length = 0;
pscholtens 2:5e37831540c7 123 previous_bit = !previous_bit;
pscholtens 1:2551859fbc25 124 }
pscholtens 6:a5fc4e2ff34b 125 /* Switch off watch dog timer */
pscholtens 6:a5fc4e2ff34b 126 timeout.detach();
pscholtens 1:2551859fbc25 127 }
pscholtens 1:2551859fbc25 128
pscholtens 1:2551859fbc25 129 /* Here we count the number of unsynchronized symbols, mimicing previous implementation */
pscholtens 1:2551859fbc25 130 unsigned int get_num_unsync_symbols(int symbol) {
pscholtens 1:2551859fbc25 131 unsigned int sum = 0;
pscholtens 1:2551859fbc25 132 for (unsigned int i = 0; i < DEPTH; i++) {
pscholtens 1:2551859fbc25 133 if (symbol == 0) {
pscholtens 1:2551859fbc25 134 sum += zeros[i];
pscholtens 1:2551859fbc25 135 } else {
pscholtens 1:2551859fbc25 136 sum += ones[i];
pscholtens 1:2551859fbc25 137 }
pscholtens 1:2551859fbc25 138 }
pscholtens 1:2551859fbc25 139 return sum;
pscholtens 1:2551859fbc25 140 }
pscholtens 1:2551859fbc25 141
pscholtens 3:8d13bf073e92 142 /* Calculate the value, using the unsynchronized method */
pscholtens 3:8d13bf073e92 143 unsigned int get_value_unsync_symbols(int symbol) {
pscholtens 3:8d13bf073e92 144 unsigned int sum = 0;
pscholtens 3:8d13bf073e92 145 for (unsigned int i = 0; i < DEPTH; i++) {
pscholtens 3:8d13bf073e92 146 if (symbol == 0) {
pscholtens 3:8d13bf073e92 147 sum += i*zeros[i];
pscholtens 3:8d13bf073e92 148 } else {
pscholtens 3:8d13bf073e92 149 sum += i*ones[i];
pscholtens 3:8d13bf073e92 150 }
pscholtens 3:8d13bf073e92 151 }
pscholtens 3:8d13bf073e92 152 return sum;
pscholtens 3:8d13bf073e92 153 }
pscholtens 3:8d13bf073e92 154
pscholtens 4:27a2eaee71ac 155 /* Calculate the value, using the synchronization algorithm */
pscholtens 4:27a2eaee71ac 156 unsigned int get_value_synced_symbols(int symbol) {
pscholtens 4:27a2eaee71ac 157 bool presence = false;
pscholtens 4:27a2eaee71ac 158 int value = 0;
pscholtens 4:27a2eaee71ac 159 for (unsigned int i = 0; i < DEPTH; i++) {
pscholtens 4:27a2eaee71ac 160 if ( zeros[i]+ones[i] != 0 ) {
pscholtens 4:27a2eaee71ac 161 if (presence) {
pscholtens 5:1c0bfd69719f 162 assign[i] = value;
pscholtens 4:27a2eaee71ac 163 } else {
pscholtens 4:27a2eaee71ac 164 value++;
pscholtens 5:1c0bfd69719f 165 presence = true;
pscholtens 5:1c0bfd69719f 166 assign[i] = value;
pscholtens 4:27a2eaee71ac 167 }
pscholtens 4:27a2eaee71ac 168 } else {
pscholtens 4:27a2eaee71ac 169 presence = false;
pscholtens 4:27a2eaee71ac 170 }
pscholtens 4:27a2eaee71ac 171 }
pscholtens 4:27a2eaee71ac 172 /* Now do the actual summation of symbol values */
pscholtens 4:27a2eaee71ac 173 unsigned int sum = 0;
pscholtens 4:27a2eaee71ac 174 for (unsigned int i = 0; i < DEPTH; i++) {
pscholtens 4:27a2eaee71ac 175 if (symbol == 0) {
pscholtens 4:27a2eaee71ac 176 sum += assign[i]*zeros[i];
pscholtens 4:27a2eaee71ac 177 } else {
pscholtens 4:27a2eaee71ac 178 sum += assign[i]*ones[i];
pscholtens 4:27a2eaee71ac 179 }
pscholtens 4:27a2eaee71ac 180 }
pscholtens 4:27a2eaee71ac 181 return sum;
pscholtens 4:27a2eaee71ac 182 }
pscholtens 4:27a2eaee71ac 183
pscholtens 4:27a2eaee71ac 184
pscholtens 1:2551859fbc25 185 /* The main routine of the program */
pscholtens 1:2551859fbc25 186
pscholtens 0:dc1b041f713e 187 int main() {
pscholtens 4:27a2eaee71ac 188 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 189 float unsync_dutycycle, synced_dutycycle, unsync_voltage, synced_voltage;
pscholtens 1:2551859fbc25 190 pc.baud(115200);
pscholtens 6:a5fc4e2ff34b 191 pc.printf("Bitstream counter, version 0.0.9, P.C.S. Scholtens, April 21th 2015, Nijmegen, Netherlands.\n");
pscholtens 6:a5fc4e2ff34b 192 pc.printf("Build " __DATE__ " " __TIME__ "\n");
pscholtens 0:dc1b041f713e 193 /*LPC_TIM2->PR = 0x0000002F; / * decimal 47 */
pscholtens 0:dc1b041f713e 194 /*LPC_TIM3->PR = 24;*/
pscholtens 1:2551859fbc25 195 clear_histogram();
pscholtens 0:dc1b041f713e 196 while(1) {
pscholtens 6:a5fc4e2ff34b 197 timer.reset();
pscholtens 0:dc1b041f713e 198 myled = 1;
pscholtens 1:2551859fbc25 199 clear_histogram();
pscholtens 6:a5fc4e2ff34b 200 timer.start();
pscholtens 5:1c0bfd69719f 201 fill_histogram(1e7);
pscholtens 6:a5fc4e2ff34b 202 timer.stop();
pscholtens 4:27a2eaee71ac 203 pc.printf("\n------ Captured Histogram ------\n");
pscholtens 1:2551859fbc25 204 print_histogram();
pscholtens 1:2551859fbc25 205 num_of_zeros = get_num_unsync_symbols(0);
pscholtens 1:2551859fbc25 206 num_of_ones = get_num_unsync_symbols(1);
pscholtens 3:8d13bf073e92 207 value_of_unsync_zeros = get_value_unsync_symbols(0);
pscholtens 3:8d13bf073e92 208 value_of_unsync_ones = get_value_unsync_symbols(1);
pscholtens 3:8d13bf073e92 209 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 210 unsync_voltage = (0.5*13*unsync_dutycycle+1)*0.9; /* This is the ADC formula, see analysisSigmaDeltaADC.pdf */
pscholtens 4:27a2eaee71ac 211 value_of_synced_zeros = get_value_synced_symbols(0);
pscholtens 4:27a2eaee71ac 212 value_of_synced_ones = get_value_synced_symbols(1);
pscholtens 4:27a2eaee71ac 213 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 214 synced_voltage = (0.5*13*synced_dutycycle+1)*0.9; /* This is the ADC formula, see analysisSigmaDeltaADC.pdf */
pscholtens 4:27a2eaee71ac 215 pc.printf("------ Unsynchronized Results ------\n");
pscholtens 4:27a2eaee71ac 216 pc.printf("Counted Sequences %8i %8i\n", num_of_zeros , num_of_ones);
pscholtens 4:27a2eaee71ac 217 pc.printf("Summed Values %8i %8i\n", value_of_unsync_zeros, value_of_unsync_ones);
pscholtens 4:27a2eaee71ac 218 pc.printf("Duty Cycle %f, equals %f Volt\n", unsync_dutycycle , unsync_voltage);
pscholtens 4:27a2eaee71ac 219 pc.printf("------ Synchronized Results ------\n");
pscholtens 4:27a2eaee71ac 220 pc.printf("Summed Values %8i %8i\n", value_of_synced_zeros, value_of_synced_ones);
pscholtens 4:27a2eaee71ac 221 pc.printf("Duty Cyle %f, equals %f Volt\n", synced_dutycycle , synced_voltage);
pscholtens 4:27a2eaee71ac 222 pc.printf("------------------------------------\n");
pscholtens 6:a5fc4e2ff34b 223 pc.printf("Measured in %f sec.\n", timer.read());
pscholtens 4:27a2eaee71ac 224 pc.printf("====================================\n");
pscholtens 0:dc1b041f713e 225 myled = 0;
pscholtens 5:1c0bfd69719f 226 wait(0.1);
pscholtens 0:dc1b041f713e 227 }
pscholtens 4:27a2eaee71ac 228 }