arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
HammingFEC.h
1#pragma once
2#include "AudioToolsConfig.h"
3#include "AudioTools/CoreAudio/BaseStream.h"
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <stdbool.h>
8#include <math.h>
9#include <string.h>
10
11namespace audio_tools {
12
29template <int bytecount, class block_t>
30class 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:37
Base class for all Streams. It relies on write(const uint8_t *buffer, size_t size) and readBytes(uint...
Definition BaseStream.h:36
Hamming forware error correction. Inspired by https://github.com/nasserkessas/hamming-codes.
Definition HammingFEC.h:30
Definition NoArduino.h:62
A simple Buffer implementation which just uses a (dynamically sized) array.
Definition Buffers.h:175
size_t setAvailable(size_t available_size)
Definition Buffers.h:273
bool write(T sample) override
write add an entry to the buffer
Definition Buffers.h:200
int availableForWrite() override
provides the number of entries that are available to write
Definition Buffers.h:232
T * data()
Provides address of actual data.
Definition Buffers.h:261
void reset() override
clears the buffer
Definition Buffers.h:263
Definition NoArduino.h:142
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10