arduino-audio-tools
HammingFEC.h
1 #pragma once
2 #include "AudioConfig.h"
3 #include "AudioTools/BaseStream.h"
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdbool.h>
8 #include <math.h>
9 #include <string.h>
10 
11 namespace audio_tools {
12 
29 template <int bytecount, class block_t>
30 class HammingFEC : public BaseStream {
31  public:
32  HammingFEC(Stream &stream){
33  p_stream = &stream;
34  p_print = &stream;
35  }
36 
37  HammingFEC(Print &print){
38  p_print = &print;
39  }
40 
41  int availableForWrite() override {
42  return bytecount;
43  }
44 
45  size_t write(const uint8_t* data, size_t len)override{
46  if (p_print==nullptr) return 0;
47  for (int j=0;j<len;j++){
48  raw.write(data[j]);
49  if(raw.availableForWrite()==0){
50  encode(raw.data(), raw.size());
51  raw.reset();
52  }
53  }
54  return len;
55  }
56 
57  int available() override{
58  return bytecount;
59  }
60 
61  size_t readBytes(uint8_t* data, size_t len)override{
62  if (p_stream==nullptr) return 0;
63  if (encoded.isEmpty()){
64  // read encoded data from surce
65  p_stream->readBytes(encoded.data(), encodedSize());
66  encoded.setAvailable(encodedSize());
67  // decode data
68  if(decode((block_t*)encoded.data(), bytecount)){
69  raw.setAvailable(bytecount);
70  }
71  }
72  return raw.readArray(data, len);
73  }
74 
75  protected:
76  SingleBuffer<uint8_t> raw{bytecount};
77  SingleBuffer<uint8_t> encoded{encodedSize()};
78  Stream* p_stream = nullptr;
79  Print* p_print = nullptr;
80 
81  int getBlocks(){
82  // Amount of bits in a block_t //
83  int bits = sizeof(block_t) * 8;
84 
85  // Amount of bits per block_t used to carry the message //
86  int messageBits = bits - log2(bits) - 1;
87 
88  // Amount of block_ts needed to encode message //
89  int block_ts = ceil((float) bytecount / messageBits);
90 
91  return block_ts;
92  }
93 
94  int encodedSize(){
95  return getBlocks()+1*sizeof(block_t);
96  }
97 
98  int encode(uint8_t *input, int len) {
99 
100  // Amount of bits in a block_t //
101  int bits = sizeof(block_t) * 8;
102 
103  // Amount of bits per block_t used to carry the message //
104  int messageBits = bits - log2(bits) - 1;
105 
106  // Amount of block_ts needed to encode message //
107  int block_ts = ceil((float) len / messageBits);
108 
109  // Array of encoded block_ts //
110  block_t encoded[block_ts+1];
111 
112  // Loop through each block_t //
113  for (int i = 0; i < block_ts+1; i++) {
114 
115  // Final encoded block_t variable //
116  block_t thisBlock = 0;
117 
118  // Amount of "skipped" bits (used for parity) //
119  int skipped = 0;
120 
121  // Count of how many bits are "on" //
122  int onCount = 0;
123 
124  // Array of "on" bits //
125  int onList[bits];
126 
127  // Loop through each message bit in this block_t to populate final block_t //
128  for (int j = 0; j < bits; j++) {
129 
130  // Skip bit if reserved for parity bit //
131  if ((j & (j - 1)) == 0) { // Check if j is a power of two or 0
132  skipped++;
133  continue;
134  }
135 
136  bool thisBit;
137 
138  if (i != block_ts) {
139 
140  // Current overall bit number //
141  int currentBit = i*messageBits + (j-skipped);
142 
143  // Current uint8_tacter //
144  int currentChar = currentBit/(sizeof(uint8_t)*8); // int division
145 
146  // Value of current bit //
147  thisBit = currentBit < len*sizeof(uint8_t)*8 ? getCharBit(input[currentChar], currentBit-currentChar*8) : 0;
148  }
149 
150  else {
151  thisBit = getBit(len/8, j-skipped+(sizeof(block_t)*8-messageBits));
152  }
153 
154  // If bit is "on", add to onList and onCount //
155  if (thisBit) {
156  onList[onCount] = j;
157  onCount++;
158  }
159 
160  // Populate final message block_t //
161  thisBlock = modifyBit(thisBlock, j, thisBit);
162  }
163 
164  // Calculate values of parity bits //
165  block_t parityBits = multipleXor(onList, onCount);
166 
167  // Loop through skipped bits (parity bits) //
168  for (int k = 1; k < skipped; k++) { // skip bit 0
169 
170  // If bit is "on", add to onCount
171  if (getBit(parityBits, sizeof(block_t)*8-skipped+k)) {
172  onCount++;
173  }
174 
175  // Add parity bit to final block_t //
176  thisBlock = modifyBit(thisBlock, (int) pow(2, skipped-k-1) , getBit(parityBits, sizeof(block_t)*8-skipped+k));
177  }
178 
179  // Add overall parity bit (total parity of onCount) //
180  thisBlock = modifyBit(thisBlock, 0, onCount & 1);
181 
182  // Add block_t to encoded block_ts //
183  encoded[i] = thisBlock;
184  }
185 
186  // Write encoded message to file /
187  return p_print->write((const uint8_t*)encoded, sizeof(block_t)*block_ts+1);
188  }
189 
190  bool decode(block_t input[], int len) {
191  uint8_t* output = (uint8_t*)raw.data();
192 
193  // Amount of bits in a block_t //
194  int bits = sizeof(block_t) * 8;
195 
196  for (int b = 0; b < (len/sizeof(block_t)); b++) {
197 
198  // Count of how many bits are "on" //
199  int onCount = 0;
200 
201  // Array of "on" bits //
202  int onList[bits];
203 
204  // Populate onCount and onList //
205  for (int i = 1; i < bits; i++) {
206  getBit(input[b], i);
207  if (getBit(input[b], i)) {
208  onList[onCount] = i;
209  onCount++;
210  }
211  }
212 
213  // Check for single errors //
214  int errorLoc = multipleXor(onList, onCount);
215 
216  if (errorLoc) {
217 
218  // Check for multiple errors //
219  if (!(onCount & 1 ^ getBit(input[b], 0))) { // last bit of onCount (total parity) XOR first bit of block_t (parity bit)
220  LOGE("\nMore than one error detected. Aborting.\n");
221  return false;
222  }
223 
224  // Flip error bit //
225  else {
226  input[b] = toggleBit(input[b], (bits-1) - errorLoc);
227  }
228  }
229  }
230 
231  // Amount of bits per block_t used to carry the message //
232  int messageBits = bits - log2(bits) - 1;
233 
234  int currentBit, currentChar;
235 
236  int uint8_ts = 0;
237 
238  for (int i = 0; i < len/sizeof(block_t); i++) {
239 
240  // Initialise variable to store amount of parity bits passed //
241  int skipped = 0;
242 
243  // Loop through each message bit in this block_t to populate final block_t //
244  for (int j = 0; j < bits; j++) {
245 
246  // Skip bit if reserved for parity bit //
247  if ((j & (j - 1)) == 0) { // Check if j is a power of two or 0
248  skipped++;
249  continue;
250  }
251 
252  // Current overall bit number //
253  currentBit = i*messageBits + (j-skipped);
254 
255  // Current uint8_tacter //
256  currentChar = currentBit/(sizeof(uint8_t)*8); // int division
257 
258  // Value of current bit //
259  bool thisBit = getBit(input[i], j);
260 
261  if (i != len/sizeof(block_t)-1) {
262 
263  // Populate final decoded uint8_tacter //
264  output[currentChar] = modifyCharBit(output[currentChar], currentBit-currentChar*sizeof(uint8_t)*8, thisBit);
265  }
266 
267  else {
268  uint8_ts = modifyBit(uint8_ts, j-skipped+(sizeof(block_t)*8-messageBits), thisBit);
269  }
270 
271  }
272  }
273 
274 
275  return uint8_ts;
276  }
277 
278  int multipleXor(int *indicies, int len) {
279  int val = indicies[0];
280  for (int i = 1; i < len; i++) {
281  val = val ^ indicies[i];
282  }
283  return val;
284  }
285 
286  block_t modifyBit(block_t n, int p, bool b) {
287  return ((n & ~(1 << (sizeof(block_t)*8-1-p))) | (b << (sizeof(block_t)*8-1-p)));
288  }
289 
290  uint8_t modifyCharBit(uint8_t n, int p, bool b) {
291  return ((n & ~(1 << (sizeof(uint8_t)*8-1-p))) | (b << (sizeof(uint8_t)*8-1-p)));
292  }
293 
294  bool getBit(block_t b, int i) {
295  return (b << i) & (int) pow(2, (sizeof(block_t)*8-1));
296  }
297 
298  bool getCharBit(uint8_t b, int i) {
299  return (b << i) & (int) pow(2, (sizeof(uint8_t)*8-1));
300  }
301 
302  block_t toggleBit(block_t b, int i) {
303  return b ^ (1 << i);
304  }
305 
306  int inList(uint8_t **list, uint8_t *testString, int listLen) {
307  for (int i = 0; i < listLen; i++) {
308  if (strcmp(list[i], testString) == 0) {
309  return i;
310  }
311  }
312  return 0;
313  }
314 };
315 
316 } // ns
virtual int readArray(T data[], int len)
reads multiple values
Definition: Buffers.h:41
Base class for all Streams. It relies on write(const uint8_t *buffer, size_t size) and readBytes(uint...
Definition: BaseStream.h:34
Hamming forware error correction. Inspired by https://github.com/nasserkessas/hamming-codes.
Definition: HammingFEC.h:30
Definition: NoArduino.h:58
T * data()
Provides address of actual data.
Definition: Buffers.h:246
size_t setAvailable(size_t available_size)
Definition: Buffers.h:258
bool write(T sample) override
write add an entry to the buffer
Definition: Buffers.h:188
int availableForWrite() override
provides the number of entries that are available to write
Definition: Buffers.h:218
void reset() override
clears the buffer
Definition: Buffers.h:248
Definition: NoArduino.h:125
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10