Skip to content

Commit

Permalink
add
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexGyver committed May 24, 2021
1 parent b4ca335 commit 23bcca6
Show file tree
Hide file tree
Showing 5 changed files with 345 additions and 0 deletions.
101 changes: 101 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)
![author](https://img.shields.io/badge/author-AlexGyver-informational.svg)
# Hamming
Библиотека для упаковки и распаковки данных по алгоритму Хэмминга (избыточные данные для восстановления)
- Порядок алгоритма 4-8 (чем выше, тем надёжнее)
- Восстановление данных, повреждённых при пересылке
- Принимает любой тип данных

### Совместимость
Совместима со всеми Arduino платформами (используются Arduino-функции)

## Содержание
- [Установка](#install)
- [Инициализация](#init)
- [Использование](#usage)
- [Пример](#example)
- [Версии](#versions)
- [Баги и обратная связь](#feedback)

<a id="install"></a>
## Установка
- Библиотеку можно найти по названию **Hamming** и установить через менеджер библиотек в:
- Arduino IDE
- Arduino IDE v2
- PlatformIO
- [Скачать библиотеку](https://github.com/GyverLibs/Hamming/archive/refs/heads/main.zip) .zip архивом для ручной установки:
- Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64)
- Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32)
- Распаковать и положить в *Документы/Arduino/libraries/*
- (Arduino IDE) автоматическая установка из .zip: *Скетч/Подключить библиотеку/Добавить .ZIP библиотеку…* и указать скачанный архив
- Читай более подробную инструкцию по установке библиотек [здесь](https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA)

<a id="init"></a>
## Инициализация
```cpp
Hamming<5> buf; // <> - порядок кода (4-8)
```

<a id="usage"></a>
## Использование
```cpp
bool pack(T &data); // запаковать данные любого типа в буфер
int unpack(uint8_t* data, int size); // распаковать данные. возврат: 0 ОК, 1 исправлены ошибки, 2 и 3 - есть неисправленные ошибки
uint8_t status(); // возврат: 0 ОК, 1 исправлены ошибки, 2 и 3 - есть неисправленные ошибки
int length(); // размер буфера (больше чем размер входных данных)
void stop(); // освободить буфер
uint8_t *buffer; // внутренний буфер
```
<a id="example"></a>
## Пример
Остальные примеры смотри в **examples**!
```cpp
#include <Hamming.h>
void setup() {
Serial.begin(9600);
// создали дату (любой тип)
char data0[] = "Hello, world! Lorem Ipsum";
// запаковали
Hamming<5> buf; // <> - порядок кода (4-8)
// пакуем во внутренний буфер buf.buffer
buf.pack(data0); // 12мс
// запакованные данные хранятся в buf.buffer с размером buf.length()
// можно их "отправить"
// ======== "ПЕРЕДАЧА" ========
// проебали часть (два байта!)
buf.buffer[5] = 0;
buf.buffer[6] = 0;
// ======== "ПРИЁМ" ========
// распаковываем. Порядок в <> такой же!
Hamming<5> buf2;
// передаём "принятые" запакованные данные и их длину (её тоже надо передать или знать)
buf2.unpack(buf.buffer, buf.length()); // 6мс
// выводим как строку
Serial.println((char*)buf2.buffer);
// выводим статус распаковки
Serial.println(buf2.status());
}
void loop() {
}
```

<a id="versions"></a>
## Версии
- v1.0

<a id="feedback"></a>
## Баги и обратная связь
При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru)
Библиотека открыта для доработки и ваших **Pull Request**'ов!
38 changes: 38 additions & 0 deletions examples/test/test.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <Hamming.h>
void setup() {
Serial.begin(9600);

// создали дату (любой тип)
char data0[] = "Hello, world! Lorem Ipsum";

// запаковали
Hamming<5> buf; // <> - порядок кода (4-8)
// пакуем во внутренний буфер buf.buffer
buf.pack(data0); // 12мс

// запакованные данные хранятся в buf.buffer с размером buf.length()
// можно их "отправить"

// ======== "ПЕРЕДАЧА" ========

// проебали часть (два байта!)
buf.buffer[5] = 0;
buf.buffer[6] = 0;

// ======== "ПРИЁМ" ========

// распаковываем. Порядок в <> такой же!
Hamming<5> buf2;

// передаём "принятые" запакованные данные и их длину (её тоже надо передать или знать)
buf2.unpack(buf.buffer, buf.length()); // 6мс

// выводим как строку
Serial.println((char*)buf2.buffer);

// выводим статус распаковки
Serial.println(buf2.status());
}

void loop() {
}
23 changes: 23 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#######################################
# Syntax Coloring Map For Hamming
#######################################

#######################################
# Datatypes (KEYWORD1)
#######################################
Hamming KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
#######################################

pack KEYWORD2
unpack KEYWORD2
status KEYWORD2
length KEYWORD2
stop KEYWORD2
buffer KEYWORD2

#######################################
# Constants (LITERAL1)
#######################################
9 changes: 9 additions & 0 deletions library.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name=Hamming
version=1.0
author=AlexGyver <alex@alexgyver.ru>
maintainer=AlexGyver <alex@alexgyver.ru>
sentence=Library for pack and unpack data by Hamming algorithm
paragraph=Library for pack and unpack data by Hamming algorithm
category=Data Processing
url=https://github.com/GyverLibs/Hamming
architectures=*
174 changes: 174 additions & 0 deletions src/Hamming.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
Библиотека для упаковки и распаковки данных по алгоритму Хэмминга (избыточные данные для восстановления)
Документация:
GitHub: https://github.com/GyverLibs/Hamming
Возможности:
- Порядок алгоритма 4-8 (чем выше, тем надёжнее)
- Восстановление данных, повреждённых при пересылке
- Принимает любой тип данных
AlexGyver, alex@alexgyver.ru
https://alexgyver.ru/
MIT License
Версии:
v1.0 - релиз
*/

#ifndef Hamming_h
#define Hamming_h
template <uint8_t HAM_SIZE = 5> // порядок алгоритма (4-8)
class Hamming {
public:
// запаковать данные в буфер
template <typename T>
bool pack(T &data) {
// 0. Считаем и создаём буфер
int size = sizeof(T); // байтов даты
uint8_t signif = chunkSizeB - (HAM_SIZE + 1); // битов даты на чанк
chunkAmount = (size * 8 + signif - 1) / signif; // колво чанков (целоч. деление)
bytes = chunkAmount * chunkSize; // размер буфера, байт
if (buffer) free(buffer); // освобождаем старый
buffer = (uint8_t*)malloc(bytes); // выделяем
if (!buffer) return 0; // не удалось создать
uint8_t buf[bytes]; // ещё буфер
memset(buf, 0, bytes); // чисти чисти
memset(buffer, 0, bytes); // чисти чисти
uint8_t *ptr = (const uint8_t*) &data; // для чтения даты
int ptrCount = 0;

for (int chunk = 0; chunk < chunkAmount; chunk++) { // каждый чанк
// 1. Заполняем дату, минуя ячейки Хэмминга (0,1,2,4,8...)
for (uint8_t i = 0; i < chunkSizeB; i++) {
if ((i & (i - 1)) != 0) { // проверка на степень двойки
write(buf, chunk * chunkSizeB + i, read(ptr, ptrCount++)); // переписываем побитно
}
}

// 2. Считаем и пишем parity для зон Хэмминга
uint8_t parityH = 0;
for (uint8_t i = 0; i < chunkSizeB; i++) {
for (uint8_t j = 0; j < HAM_SIZE; j++) {
// если это ячейка хэмминга и бит стоит, инвертируем текущий parity
if ((i & (1 << j)) && read(buf, chunk * chunkSizeB + i)) parityH ^= (1 << j);
}
}
for (uint8_t i = 0; i < HAM_SIZE; i++) {
write(buf, chunk * chunkSizeB + (1 << i), (parityH >> i) & 1); // переписываем parity ячеек хэмминга
}

// 3. Считаем и пишем общий parity
uint8_t count = 0;
for (uint8_t i = 1; i < chunkSizeB; i++) {
if (read(buf, chunk * chunkSizeB + i)) count++; // считаем
}
write(buf, chunk * chunkSizeB, count & 1); // пишем
}

// 4. Перемешиваем
for (uint8_t i = 0, k = 0; i < chunkSizeB; i++) {
for (uint8_t j = 0; j < chunkAmount; j++) {
write(buffer, k++, read(buf, i + j * chunkSizeB));
}
}
return 1;
}

// распаковать данные
// возврат: 0 ОК, 1 исправлены ошибки, 2 и 3 - есть неисправленные ошибки
int unpack(uint8_t* data, int size) {
// 0. Считаем и создаём буфер
uint8_t signif = chunkSizeB - (HAM_SIZE + 1); // битов даты на чанк
if (size & chunkSize != 0) return 0; // не кратно размеру чанка
chunkAmount = size / chunkSize; // колво чанков
bytes = chunkAmount * signif / 8; // размер буфера, байт (округл. вниз)
if (buffer) free(buffer); // чисти старый
buffer = (uint8_t*)malloc(bytes); // выделяем
if (!buffer) return 0; // не удалось создать
memset(buffer, 0, bytes); // чисти чисти
uint8_t buf[size];
int ptrCount = 0;
stat = 0;

// 1. Разбираем мешанину обратно
for (uint8_t i = 0, k = 0; i < chunkSizeB; i++) {
for (uint8_t j = 0; j < chunkAmount; j++) {
write(buf, i + j * chunkSizeB, read(data, k++));
}
}

for (int chunk = 0; chunk < chunkAmount; chunk++) { // каждый чанк
// 2. Получаем хэш ошибки и общий parity
uint8_t sum = 0, count = 0;
for (uint8_t i = 0; i < chunkSizeB; i++) {
if (read(buf, chunk * chunkSizeB + i)) {
sum ^= i;
if (i > 0) count++;
}
}

// 3. Анализируем результат
if (sum != 0) { // 1+ err
if (read(buf, chunk * chunkSizeB) == (count & 1)) stat = max(stat, 2); // 2 err
else toggle(buf, chunk * chunkSizeB + sum); // fix err
stat = max(stat, 1);
} else {
if (read(buf, chunk * chunkSizeB) != (count & 1)) stat = max(stat, 3); // parity err
}

// 4. Собираем дату из ячеек Хэмминга
for (uint8_t i = 0; i < chunkSizeB; i++) {
if ((i & (i - 1)) != 0) { // проверка на степень двойки
write(buffer, ptrCount++, read(buf, chunk * chunkSizeB + i)); // переписываем побитно
}
}
}
return stat;
}

// возврат: 0 ОК, 1 исправлены ошибки, 2 и 3 - есть неисправленные ошибки
uint8_t status() {
return stat;
}

// размер буфера (больше чем размер входных данных)
int length() {
return bytes;
}

// деструктор
~Hamming() {
stop();
}

// освободить буфер
void stop() {
if (buffer) free(buffer);
}

// внутренний буфер
uint8_t *buffer = NULL;

private:
void set(uint8_t* buf, int num) {
bitSet(buf[num >> 3], num & 0b111);
}
void clear(uint8_t* buf, int num) {
bitClear(buf[num >> 3], num & 0b111);
}
void write(uint8_t* buf, int num, bool state) {
state ? set(buf, num) : clear(buf, num);
}
bool read(uint8_t* buf, int num) {
return bitRead(buf[num >> 3], num & 0b111);
}
void toggle(uint8_t* buf, int num) {
read(buf, num) ? clear(buf, num) : set(buf, num);
}
uint8_t stat;
int bytes = 0;
int chunkAmount = 0;
const uint8_t chunkSizeB = (1 << HAM_SIZE); // вес чанка в битах
const uint8_t chunkSize = (1 << HAM_SIZE) >> 3; // вес чанка в байтах
};
#endif

0 comments on commit 23bcca6

Please sign in to comment.