├── .gitattributes ├── .github └── workflows │ └── tg-send.yml ├── LICENSE ├── README.md ├── README_EN.md ├── examples ├── old │ └── old.ino └── test │ └── test.ino ├── keywords.txt ├── library.properties └── src ├── Hamming.h └── HammingOld.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/tg-send.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Telegram Message 3 | on: 4 | release: 5 | types: [published] 6 | jobs: 7 | build: 8 | name: Send Message 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: send telegram message on push 12 | uses: appleboy/telegram-action@master 13 | with: 14 | to: ${{ secrets.TELEGRAM_TO }} 15 | token: ${{ secrets.TELEGRAM_TOKEN }} 16 | disable_web_page_preview: true 17 | message: | 18 | ${{ github.event.repository.name }} v${{ github.event.release.tag_name }} 19 | ${{ github.event.release.body }} 20 | https://github.com/${{ github.repository }} 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 AlexGyver 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![latest](https://img.shields.io/github/v/release/GyverLibs/Hamming.svg?color=brightgreen)](https://github.com/GyverLibs/Hamming/releases/latest/download/Hamming.zip) 2 | [![PIO](https://badges.registry.platformio.org/packages/gyverlibs/library/Hamming.svg)](https://registry.platformio.org/libraries/gyverlibs/Hamming) 3 | [![Foo](https://img.shields.io/badge/Website-AlexGyver.ru-blue.svg?style=flat-square)](https://alexgyver.ru/) 4 | [![Foo](https://img.shields.io/badge/%E2%82%BD%24%E2%82%AC%20%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B0%D1%82%D1%8C-%D0%B0%D0%B2%D1%82%D0%BE%D1%80%D0%B0-orange.svg?style=flat-square)](https://alexgyver.ru/support_alex/) 5 | [![Foo](https://img.shields.io/badge/README-ENGLISH-blueviolet.svg?style=flat-square)](https://github-com.translate.goog/GyverLibs/Hamming?_x_tr_sl=ru&_x_tr_tl=en) 6 | 7 | [![Foo](https://img.shields.io/badge/ПОДПИСАТЬСЯ-НА%20ОБНОВЛЕНИЯ-brightgreen.svg?style=social&logo=telegram&color=blue)](https://t.me/GyverLibs) 8 | 9 | # Hamming 10 | Библиотека для упаковки и распаковки данных по алгоритму Хэмминга (Extended Hamming) 11 | - Порядок алгоритма (кол-во бит Хэмминга) 3-7 12 | - Восстановление данных, повреждённых при пересылке: восстановление 1 бита на блок, либо определение невозможности восстановления 13 | - Опционально перемешивание данных для более надёжной передачи - может восстановить несколько испорченных бит/байт подряд 14 | 15 | > Библиотека обновлена до версии v2, несовместима со старой! Старую версию можно подключить как `#include ` 16 | 17 | ### Совместимость 18 | Совместима со всеми Arduino платформами (используются Arduino-функции) 19 | 20 | ## Содержание 21 | - [Документация](#doc) 22 | - [Пример](#example) 23 | - [Версии](#versions) 24 | - [Установка](#install) 25 | - [Баги и обратная связь](#feedback) 26 | 27 | 28 | 29 | ## Документация 30 | ### База 31 | Новая версия библиотеки содержит несколько классов по количеству избыточных данных пакета: 32 | 33 | | Класс | Размер блока, байт | Размер блока, бит | Данных в блоке, бит | Размер пакета 100 байт, байт | 34 | | ---------- | ------------------ | ----------------- | ------------------- | ---------------------------- | 35 | | `Hamming3` | 1 | 8 | 4 | 200 | 36 | | `Hamming4` | 2 | 16 | 11 | 146 | 37 | | `Hamming5` | 4 | 32 | 26 | 124 | 38 | | `Hamming6` | 8 | 64 | 57 | 120 | 39 | | `Hamming7` | 16 | 128 | 120 | 112 | 40 | 41 | Чем выше класс, тем: 42 | 43 | - Больше размер блока (минимальный размер пакета) 44 | - Меньше итоговый размер пакета, т.к. выше соотношение битов данных к размеру блока 45 | - Дольше обработка и ниже надёжность 46 | 47 | ### Бенчмарк 48 | Тест проводился на AVR ATmega328p 16MHz, время в мкс. 49 | 50 | #### Hamming4 51 | | Размер пакета | `encode` | `mix` | `mix8` | `decode` | 52 | | ------------- | -------- | ----- | ------ | -------- | 53 | | 25 | 1673 | 1557 | 486 | 2811 | 54 | | 50 | 3315 | 3034 | 1078 | 5469 | 55 | | 100 | 7240 | 5977 | 2185 | 10794 | 56 | 57 | > Версии 4/5/6/7 не сильно отличаются по времени 58 | 59 | #### Hamming3 60 | | Размер пакета | `encode` | `mix` | `mix8` | `decode` | 61 | | ------------- | -------- | ----- | ------ | -------- | 62 | | 25 | 94 | 1933 | 689 | 230 | 63 | | 50 | 185 | 3847 | 1373 | 472 | 64 | | 100 | 366 | 7690 | 2741 | 944 | 65 | 66 | > `Hamming3` реализован таблично и работает многократно быстрее остальных 67 | 68 | ### Функции 69 | ```cpp 70 | // размер запакованных данных по размеру исходных 71 | size_t encodedSize(size_t size); 72 | 73 | // размер распакованных данных по размеру запакованных. 0 - некорректный размер пакета 74 | size_t decodedSize(size_t size); 75 | 76 | // замешать запакованные данные. false если ошибка аллокации 77 | bool mix(void* pack, size_t size); 78 | 79 | // размешать запакованные данные. false если ошибка аллокации 80 | bool unmix(void* pack, size_t size); 81 | 82 | // замешать запакованные данные. false если ошибка аллокации 83 | bool mix8(void* pack, size_t size); 84 | 85 | // размешать запакованные данные. false если ошибка аллокации 86 | bool unmix8(void* pack, size_t size); 87 | 88 | // Запаковать данные во внешний буфер dest размера encodedSize() [должен быть инициализирован 0] 89 | void encode(void* dest, const void* src, size_t size); 90 | 91 | // Распаковать данные в себя. Вернёт true, если распакованы без ошибок или ошибки исправлены 92 | bool decode(void* src, size_t size); 93 | ``` 94 | 95 | ### Использование 96 | - Взяли данные, получили размер буфера (больше чем сами данные), закодировали данные в него 97 | - "Передали" буфер на другое устройство 98 | - Раскодировали полученные данные. Если в процессе передачи данные были повреждены - они будут сами по возможности восстановлены 99 | 100 | > [!CAUTION] 101 | > Так как после кодирования пакет имеет размер, кратный размеру блока при выбранном порядке, то реальный размер пакета будет утерян - при распаковке он посчитается математически. Например `Hamming5`, размер данных 10 байт. Размер пакета получится 16 байт (округлится в большую сторону), а при распаковке получится 13 байт. Реальный размер пакета нужно знать на стороне приёмника или добавить в сам пакет, библиотека этого не делает. 102 | 103 | ### Надёжность 104 | Алгоритм может восстановить один "испорченный" бит в блоке. Чем меньше размер блока, тем больший "шум" потенциально сможет пережить пакет при передаче. Блоки идут друг за другом, поэтому если повредить два соседних бита в пределах одного блока - данные восстановить не получится. 105 | 106 | В режиме `Hamming3` пакет занимает размер двойных данных, т.е. это как отправить данные дважды. Но при наличии помех данные будут просто испорчены, сколько пакетов не отправляй. А с Hamming можно потерять приличную часть пакета (12.5% в данном случае) и данные получится восстановить. 107 | 108 | > При создании своих протоколов связи рекомендуется добавлять в пакет CRC и размер пакета, как например в библиотеке [GyverWire](https://github.com/GyverLibs/GyverWire) 109 | 110 | ### Перемешивание 111 | Если "перемешать" пакет (расположить биты блоков по очереди друг за другом), то после размешивания можно восстановить несколько испорченных бит подряд, или даже байтов - зависит от размера пакета, т.к. ошибка затронет разные блоки. Пакет перемешивается после упаковки (перед отправкой) и размешивается обратно (после получения) перед распаковкой. Перемешивание и размешивание пакета - долгая операция и выделяет память размером с буфер, добавляется вручную на усмотрение пользователя. 112 | 113 | Доступно два алгоритма перемешивания - полный `mix`/`unmix` и 8-байтный `mix8`/`unmix8`. Полный выполняется долго и полностью растягивает блоки по пакету. 8-байтный работает блоками по 8 байт, остаток замешивает по остатку. Полное перемешивание позволяет восстановить несколько испорченных байт подряд (при большом размере пакета), а 8-битное - только 1 байт на каждые 8 байт. С одиночными испорченными битами они работают одинаково. 114 | 115 | 116 | 117 | ## Пример 118 | ```cpp 119 | #include 120 | 121 | void setup() { 122 | Serial.begin(115200); 123 | Serial.println("=== START ==="); 124 | 125 | // создали дату (любой тип) 126 | char str[] = "Hello, world! Hamming encoding"; 127 | 128 | Serial.print("data len: "), Serial.println(sizeof(str)); 129 | 130 | // размер запакованных данных 131 | size_t elen = Hamming4::encodedSize(sizeof(str)); 132 | 133 | Serial.print("pack len: "), Serial.println(elen); 134 | 135 | // буфер для пакета 136 | uint8_t p[elen] = {}; 137 | 138 | // упаковка 139 | Hamming4::encode(p, str, sizeof(str)); 140 | 141 | // замешивание 142 | Hamming4::mix8(p, elen); 143 | // Hamming4::mix(p, elen); 144 | 145 | // ======== ПЕРЕДАЧА ======== 146 | // имитация порчи данных 147 | 148 | p[3] = 0; // байт 149 | // bitWrite(p[9], 3, !bitRead(p[9], 3)); // бит 150 | 151 | // ======== ПЕРЕДАЧА ======== 152 | 153 | // размешивание 154 | Hamming4::unmix8(p, elen); 155 | // Hamming4::unmix(p, elen); 156 | 157 | // размер распакованных данных 158 | size_t dlen = Hamming4::decodedSize(sizeof(p)); 159 | 160 | Serial.print("decode len: "), Serial.println(dlen); 161 | 162 | // распаковка (в этот же буфер) 163 | bool res = Hamming4::decode(p, sizeof(p)); 164 | 165 | if (res) Serial.write((char*)p, dlen), Serial.println(); 166 | else Serial.println("Data error!"); 167 | 168 | Serial.println("==== END ===="); 169 | } 170 | ``` 171 | 172 | 173 | 174 | ## Версии 175 | - v1.0 176 | - v1.1 - исправлена критическая ошибка 177 | - v1.2 - добавлена bool pack(uint8_t *ptr, uint32_t size) 178 | - v1.3 - исправлена критическая ошибка 179 | - v1.3.1 - мелкие улучшения 180 | - v2.0.0 - сильная оптимизация скорости и памяти, новые инструменты 181 | 182 | 183 | 184 | ## Установка 185 | - Библиотеку можно найти по названию **Hamming** и установить через менеджер библиотек в: 186 | - Arduino IDE 187 | - Arduino IDE v2 188 | - PlatformIO 189 | - [Скачать библиотеку](https://github.com/GyverLibs/Hamming/archive/refs/heads/main.zip) .zip архивом для ручной установки: 190 | - Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64) 191 | - Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32) 192 | - Распаковать и положить в *Документы/Arduino/libraries/* 193 | - (Arduino IDE) автоматическая установка из .zip: *Скетч/Подключить библиотеку/Добавить .ZIP библиотеку…* и указать скачанный архив 194 | - Читай более подробную инструкцию по установке библиотек [здесь](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) 195 | ### Обновление 196 | - Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи 197 | - Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить" 198 | - Вручную: **удалить папку со старой версией**, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам! 199 | 200 | 201 | 202 | ## Баги и обратная связь 203 | При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru) 204 | Библиотека открыта для доработки и ваших **Pull Request**'ов! 205 | 206 | При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать: 207 | - Версия библиотеки 208 | - Какой используется МК 209 | - Версия SDK (для ESP) 210 | - Версия Arduino IDE 211 | - Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде 212 | - Какой код загружался, какая работа от него ожидалась и как он работает в реальности 213 | - В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | This is an automatic translation, may be incorrect in some places. See sources and examples! 2 | 3 | # Hamming 4 | Library for packaging and unpacking data on the Hamming algorithm (Extnded Hamming) 5 | -the order of the algorithm (number of bits of Hamming) 3-7 6 | - restoration of data damaged during shipping: restoration 1 bit per block, or determining the impossibility of restoring 7 | - optionally mixing data for a more reliable transmission - can restore several spoiled bit/byte in a row 8 | 9 | > The library is updated to version V2, incompatible with the old one!The old version can be connected as `# Include ` 10 | 11 | ## compatibility 12 | Compatible with all arduino platforms (used arduino functions) 13 | 14 | ## Content 15 | - [documentation] (#doc) 16 | - [Example] (# Example) 17 | - [versions] (#varsions) 18 | - [installation] (# Install) 19 | - [bugs and feedback] (#fedback) 20 | 21 | 22 | 23 | ## Documentation 24 | ### base 25 | The new version of the library contains several classes in terms of the number of excess package data: 26 | 27 | |Class |Block size, byte |Block size, bit |Data in the block, bit |Package size 100 bytes, byte | 28 | |---------- |------------------ |----------------- |------------------- |--------------------------- | 29 | |`Hamming3` |1 |8 |4 |200 | 30 | |`Hamming4` |2 |16 |11 |146 | 31 | |`Hamming5` |4 |32 |26 |124 | 32 | |`Hamming6` |8 |64 |57 |120 | 33 | |`Hamming7` |16 |128 |120 |112 | 34 | 35 | The higher the class, the: 36 | 37 | - larger block size (minimum package size) 38 | - less the final size of the package, becauseabove the ratio of data bits to the size of the block 39 | - longer processing and below reliability 40 | 41 | ### Benchmark 42 | The test was held on AVR Atmega328p 16MHz, time in the ISS. 43 | 44 | ### Hamming4 45 | |Package size |`encode` |`mix` |`mix8` |`decode` | 46 | |------------- |-------- |----- |------ |-------- | 47 | |25 |1673 |1557 |486 |2811 | 48 | |50 |3315 |3034 |1078 |5469 | 49 | |100 |7240 |5977 |2185 |10794 | 50 | 51 | > Versions 4/5/6/7 are not very different in time 52 | 53 | ### Hamming3 54 | |Package size |`encode` |`mix` |`mix8` |`decode` | 55 | |------------- |-------- |----- |------ |-------- | 56 | |25 |94 |1933 |689 |230 | 57 | |50 |185 |3847 |1373 |472 | 58 | |100 |366 |7690 |2741 |944 | 59 | 60 | > `Hamming3` implemented tablely and works many times faster than the rest 61 | 62 | ### functions 63 | `` `CPP 64 | // size of packed data in terms of source 65 | Size_T ENCODEDSIZE (Size_T SIZE); 66 | 67 | // The size of the unpacked data by the size of packed.0 - incorrect package size 68 | Size_t DecededSize (Size_t Size); 69 | 70 | // Knock the packed data.FALSE if an allocation error 71 | Bool Mix (VOID* Pack, Size_t Size); 72 | 73 | // Stir the packed data.FALSE if an allocation error 74 | Bool Unmix (VOID*Pack, Size_t Size); 75 | 76 | // Knock the packed data.FALSE if an allocation error 77 | Bool MIX8 (VOID* PACK, SIZE_T SIZE); 78 | 79 | // Stir the packed data.FALSE if an allocation error 80 | Bool Unmix8 (Void* Pack, Size_t Size); 81 | 82 | // Pull data in the external buffer of the size of the EncodeedSize () [should be initialized 0] 83 | VOID ENCODE (VOID* Dest, const VOID* SRC, SIZE_T SIZE); 84 | 85 | // unpack the data into yourself.Will return True if it is unpacked without errors or errors corrected 86 | Bool decode (VOID* SRC, SIZE_T SIZE); 87 | `` ` 88 | 89 | ### Usage 90 | - took the data, received the size of the buffer (more than the data themselves), encoded the data in it 91 | - "handed over" the buffer to another device 92 | - We divorced the data obtained.If the data was damaged in the process of transmission, they themselves will be restored if possible 93 | 94 | > [! Caution] 95 | > Since after encoding the package has a size multiple of the size of the block in the selected manner, the real size of the package will be lost - when unpacking it is calculated mathematically.For example, `Hamming5`, data size 10 bytes.The size of the package will turn out 16 bytes (widened in a large way), and when unpacking will turn out 13 bytes.The real size of the package must be known on the receiver side or add to the package itself, the library does not. 96 | 97 | ## reliability 98 | The algorithm can restore one "spoiled" bit in the block.The smaller the size of the block, the greater the "noise" potentially can survive the package when transmitting.Blocks go one after another, so if you damage two neighboring bats within the same block, the data cannot be restored. 99 | 100 | In `Hamming3 'mode, the package has a double data size, i.e.This is how to send data twice.But if there are interference, the data will simply be spoiled, how many packages do not send.And with Hamming you can lose a decent part of the package (12.5% ​​in this case) and the data will be restored. 101 | 102 | > When creating their communication protocols, it is recommended to add CRC and packet size, such as in the library [gyverwire] (https://github.com/gyverlibs/gyverwire) 103 | 104 | ### Mixing 105 | If you “mix” the package (place the blocks of the blocks in turn one after another), then after stirring you can restore several spoiled bit in a row, or even bytes, depends on the size of the package, becauseThe error will affect different blocks.The package is mixed after packaging (before sending) and stirred back (after receiving) before unpacking.Mixing and stirring the package - a long operation and selects memory the size of a buffer, is added manually at the discretion of the user. 106 | 107 | Two mixing algorithms are available - complete `mix`/` unmix` and 8 -back `mix8`/` unmix8`.The full is performed for a long time and completely stretches the blocks on the package.8-byte works 8 byte blocks, the remainder kneads by the balance.Full mixing allows you to restore several spoiled bytes in a row (with a large package size), and 8 -bit - only 1 bytes for every 8 bytes.With single spoiled bats, they work the same way. 108 | 109 | 110 | 111 | ## Example 112 | `` `CPP 113 | #include 114 | 115 | VOID setup () { 116 | Serial.Begin (115200); 117 | Serial.println ("=== Start ==="); 118 | 119 | // Created a date (any type) 120 | Chard [] = "Hello, World! Hamming Encoding"; 121 | 122 | Serial.print ("Data Len:"), serial.println (SIZEOF (str)); 123 | 124 | // Size of packed data 125 | Size_t Elen = Hamming4 :: EncodeedSize (Sizeof (Str)); 126 | 127 | Serial.print ("Pack Len:"), serial.println (Elen); 128 | 129 | // buffer for package 130 | uint8_t p [elen] = {}; 131 | 132 | // package 133 | Hamming4 :: Encode (p, str, sizeof (str)); 134 | 135 | // knitting 136 | Hamming4 :: mix8 (p, elen); 137 | // hamming4 :: mix (p, elen); 138 | 139 | // ======== Transmission =============== 140 | // imitation of data damage 141 | 142 | P [3] = 0;// Bayt 143 | // bitwrite (p [9], 3,! Bitread (p [9], 3));// bit 144 | 145 | // ======== Transmission =============== 146 | 147 | // Stiring 148 | Hamming4 :: unmix8 (P, Elen); 149 | // hamming4 :: unmix (p, elen); 150 | 151 | // Size of unpacked data 152 | Size_t Dlen = Hamming4 :: DecDedsize (Sizeof (P)); 153 | 154 | Serial.print ("Decode Len:"), serial.println (Dlen); 155 | 156 | // unpacking (in the same buffer) 157 | Bool rES = Hamming4 :: Decode (p, sizeof (p)); 158 | 159 | if (res) serial.write ((char*) p, dlen), serial.println (); 160 | Else serial.println ("Data Error!"); 161 | 162 | Serial.println ("==== end ===="); 163 | } 164 | `` ` 165 | 166 | 167 | 168 | ## versions 169 | - V1.0 170 | - V1.1 - Critical error is fixed 171 | - V1.2 - Added Bool Pack (Uint8_t *PTR, Uint32_T SIZE) 172 | - V1.3 - Critical error is fixed 173 | - v1.3.1 - minor improvements 174 | - v2.0.0 - strong optimization of speed and memory, new tools 175 | 176 | 177 | 178 | ## Installation 179 | - The library can be found by the name ** Hamming ** and installed through the library manager in: 180 | - Arduino ide 181 | - Arduino ide v2 182 | - Platformio 183 | - [download the library] (https://github.com/gyverlibs/hamming/archive/refs/heads/main.zip). Zip archive for manual installation: 184 | - unpack and put in * C: \ Program Files (X86) \ Arduino \ Libraries * (Windows X64) 185 | - unpack and put in * C: \ Program Files \ Arduino \ Libraries * (Windows X32) 186 | - unpack and put in *documents/arduino/libraries/ * 187 | - (Arduino id) Automatic installation from. Zip: * sketch/connect the library/add .Zip library ... * and specify downloaded archive 188 | - Read more detailed instructions for installing libraries[here] (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%B8%D0%BE%D1%82%D0%B5%D0%BA) 189 | ### Update 190 | - I recommend always updating the library: errors and bugs are corrected in the new versions, as well as optimization and new features are added 191 | - through the IDE library manager: find the library how to install and click "update" 192 | - Manually: ** remove the folder with the old version **, and then put a new one in its place.“Replacement” cannot be done: sometimes in new versions, files that remain when replacing are deleted and can lead to errors! 193 | 194 | 195 | 196 | ## bugs and feedback 197 | Create ** Issue ** when you find the bugs, and better immediately write to the mail [alex@alexgyver.ru] (mailto: alex@alexgyver.ru) 198 | The library is open for refinement and your ** pull Request ** 'ow! 199 | 200 | When reporting about bugs or incorrect work of the library, it is necessary to indicate: 201 | - The version of the library 202 | - What is MK used 203 | - SDK version (for ESP) 204 | - version of Arduino ide 205 | - whether the built -in examples work correctly, in which the functions and designs are used, leading to a bug in your code 206 | - what code has been loaded, what work was expected from it and how it works in reality 207 | - Ideally, attach the minimum code in which the bug is observed.Not a canvas of a thousand lines, but a minimum code -------------------------------------------------------------------------------- /examples/old/old.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void setup() { 4 | Serial.begin(9600); 5 | 6 | // создали дату (любой тип) 7 | char data0[] = "Hello, world! Lorem Ipsum"; 8 | 9 | // запаковали 10 | Hamming<5> buf; // <> - порядок кода (4-7) 11 | // пакуем во внутренний буфер buf.buffer 12 | buf.pack(data0); // 12мс 13 | 14 | // запакованные данные хранятся в buf.buffer с размером buf.length() 15 | // можно их "отправить" 16 | 17 | // ======== "ПЕРЕДАЧА" ======== 18 | 19 | // про@бали часть (два байта!) 20 | buf.buffer[5] = 0; 21 | buf.buffer[6] = 0; 22 | 23 | // ======== "ПРИЁМ" ======== 24 | 25 | // распаковываем. Порядок в <> такой же! 26 | Hamming<5> buf2; 27 | 28 | // передаём "принятые" запакованные данные и их длину (её тоже надо передать или знать) 29 | buf2.unpack(buf.buffer, buf.length()); // 6мс 30 | 31 | // выводим как строку 32 | Serial.println((char*)buf2.buffer); 33 | 34 | // выводим статус распаковки 35 | Serial.println(buf2.status()); 36 | } 37 | 38 | void loop() { 39 | } 40 | -------------------------------------------------------------------------------- /examples/test/test.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void setup() { 5 | Serial.begin(115200); 6 | Serial.println("=== START ==="); 7 | 8 | // создали дату (любой тип) 9 | char str[] = "Hello, world! Hamming encoding"; 10 | 11 | Serial.print("data len: "), Serial.println(sizeof(str)); 12 | 13 | // размер запакованных данных 14 | size_t elen = Hamming4::encodedSize(sizeof(str)); 15 | 16 | Serial.print("pack len: "), Serial.println(elen); 17 | 18 | // буфер для пакета 19 | uint8_t p[elen] = {}; 20 | 21 | // упаковка 22 | Hamming4::encode(p, str, sizeof(str)); 23 | 24 | // замешивание 25 | Hamming4::mix8(p, elen); 26 | // Hamming4::mix(p, elen); 27 | 28 | // ======== ПЕРЕДАЧА ======== 29 | // имитация порчи данных 30 | 31 | p[3] = 0; // байт 32 | // bitWrite(p[9], 3, !bitRead(p[9], 3)); // бит 33 | 34 | // ======== ПЕРЕДАЧА ======== 35 | 36 | // размешивание 37 | Hamming4::unmix8(p, elen); 38 | // Hamming4::unmix(p, elen); 39 | 40 | // размер распакованных данных 41 | size_t dlen = Hamming4::decodedSize(sizeof(p)); 42 | 43 | Serial.print("unpack len: "), Serial.println(dlen); 44 | 45 | // распаковка (в этот же буфер) 46 | bool res = Hamming4::decode(p, sizeof(p)); 47 | 48 | if (res) Serial.write((char*)p, dlen), Serial.println(); 49 | else Serial.println("Data error!"); 50 | 51 | Serial.println("==== END ===="); 52 | } 53 | 54 | void loop() { 55 | } -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Hamming 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | Hamming KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions (KEYWORD2) 12 | ####################################### 13 | 14 | ####################################### 15 | # Constants (LITERAL1) 16 | ####################################### -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Hamming 2 | version=2.0.1 3 | author=AlexGyver 4 | maintainer=AlexGyver 5 | sentence=Library for pack and unpack data by Hamming algorithm 6 | paragraph=Library for pack and unpack data by Hamming algorithm 7 | category=Data Processing 8 | url=https://github.com/GyverLibs/Hamming 9 | architectures=* -------------------------------------------------------------------------------- /src/Hamming.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #ifdef __AVR__ 7 | #include 8 | #endif 9 | 10 | // =============== UTILS =============== 11 | 12 | #define HM_BEGIN_MIX \ 13 | uint8_t* buf = new uint8_t[size](); \ 14 | if (!buf) return false; 15 | 16 | #define HM_END_MIX \ 17 | memcpy(pack, buf, size); \ 18 | delete[] buf; \ 19 | return true; 20 | 21 | namespace hamm { 22 | 23 | static void mix8(uint8_t* buf, uint8_t* pack, size_t size) { 24 | // for (size_t i = 0; i < size; i += 8) { 25 | // for (uint8_t bit = 0; bit < 8; bit++) { 26 | // for (uint8_t byte = 0; byte < 8; byte++) { 27 | // // if ((pack[byte + i] >> bit) & 1) buf[bit + i] |= (1 << byte); 28 | // buf[bit + i] >>= 1; 29 | // if (pack[byte + i] & 1) buf[bit + i] |= (1 << 7); 30 | // pack[byte + i] >>= 1; 31 | // } 32 | // } 33 | // } 34 | 35 | uint8_t n = 8; 36 | for (size_t i = 0; i < size; i += 8) { 37 | uint8_t top = i + 8 > size ? size - i : 8; 38 | for (uint8_t bit = 0; bit < 8; bit++) { 39 | for (uint8_t byte = 0; byte < top; byte++) { 40 | *buf >>= 1; 41 | if ((pack[byte + i] >> bit) & 1) *buf |= (1 << 7); 42 | if (!--n) n = 8, ++buf; 43 | } 44 | } 45 | } 46 | } 47 | static void unmix8(uint8_t* buf, uint8_t* pack, size_t size) { 48 | // for (size_t i = 0; i < size; i += 8) { 49 | // for (uint8_t bit = 0; bit < 8; bit++) { 50 | // for (uint8_t byte = 0; byte < 8; byte++) { 51 | // // if ((pack[bit + i] >> byte) & 1) buf[byte + i] |= (1 << bit); 52 | // buf[byte + i] >>= 1; 53 | // if (pack[bit + i] & 1) buf[byte + i] |= (1 << 7); 54 | // pack[bit + i] >>= 1; 55 | // } 56 | // } 57 | // } 58 | 59 | uint8_t n = 8; 60 | for (size_t i = 0; i < size; i += 8) { 61 | uint8_t top = i + 8 > size ? size - i : 8; 62 | for (uint8_t bit = 0; bit < 8; bit++) { 63 | for (uint8_t byte = 0; byte < top; byte++) { 64 | if (*pack & 1) buf[byte + i] |= (1 << bit); 65 | *pack >>= 1; 66 | if (!--n) n = 8, ++pack; 67 | } 68 | } 69 | } 70 | } 71 | 72 | #ifdef __AVR__ 73 | static const uint8_t table84[] PROGMEM = {0x0, 0xF, 0x33, 0x3C, 0x55, 0x5A, 0x66, 0x69, 0x96, 0x99, 0xA5, 0xAA, 0xC3, 0xCC, 0xF0, 0xFF}; 74 | 75 | #else 76 | static const uint8_t table84[] = {0x0, 0xF, 0x33, 0x3C, 0x55, 0x5A, 0x66, 0x69, 0x96, 0x99, 0xA5, 0xAA, 0xC3, 0xCC, 0xF0, 0xFF}; 77 | #endif 78 | 79 | } // namespace hamm 80 | 81 | // =============== UTILS =============== 82 | 83 | template // порядок алгоритма (4-7) 84 | class HammingT { 85 | public: 86 | static constexpr size_t BLOCK_SIZE = ((1 << HAM_SIZE) >> 3); // вес блока в байтах (2-16) 87 | static constexpr size_t BLOCK_SIZE_B = (1 << HAM_SIZE); // вес блока в битах (16-128) 88 | static constexpr size_t BLOCK_DATA_B = BLOCK_SIZE_B - (HAM_SIZE + 1); // битов данных на блок (11-120) 89 | 90 | // размер запакованных данных по размеру исходных 91 | static constexpr size_t encodedSize(size_t size) { 92 | return ((size * 8 + BLOCK_DATA_B - 1) / BLOCK_DATA_B) * BLOCK_SIZE; 93 | } 94 | 95 | // размер распакованных данных по размеру запакованных. 0 - некорректный размер пакета 96 | static constexpr size_t decodedSize(size_t size) { 97 | return (size & (BLOCK_SIZE - 1)) ? 0 : ((size / BLOCK_SIZE) * BLOCK_DATA_B) >> 3; 98 | } 99 | 100 | // Запаковать данные во внешний буфер dest размера encodedSize() [должен быть инициализирован 0] 101 | static void encode(void* dest, const void* src, size_t size) { 102 | size_t maxB = ((size * 8 + BLOCK_DATA_B - 1) / BLOCK_DATA_B) * BLOCK_SIZE_B; 103 | size_t bit = 0, maxBit = size * 8; 104 | 105 | for (size_t b = 0; b < maxB; b += BLOCK_SIZE_B) { 106 | uint8_t count = 0, parity = 0; 107 | 108 | for (uint8_t i = 0; i < BLOCK_SIZE_B; i++) { 109 | // 1. Заполняем дату, минуя ячейки Хэмминга (0,1,2,4,8...) 110 | if ((i & (i - 1)) && (bit < maxBit) && _readB(src, bit++)) { 111 | ++count; 112 | _setB(dest, b + i); 113 | 114 | // 2. Считаем и пишем parity для зон Хэмминга. Если это ячейка хэмминга и бит стоит, инвертируем parity 115 | for (uint8_t j = 0; j < HAM_SIZE; j++) { 116 | uint8_t v = 1 << j; 117 | if (i & v) parity ^= v; 118 | } 119 | } 120 | } 121 | 122 | for (uint8_t i = 0; i < HAM_SIZE; i++) { 123 | if ((parity >> i) & 1) { 124 | ++count; // Записываем биты Хемминга 125 | _setB(dest, b + (1 << i)); // записываем parity ячеек хэмминга 126 | } 127 | } 128 | 129 | // 3. Пишем общий parity для блока 130 | if (count & 1) _setB(dest, b); 131 | } 132 | } 133 | 134 | // Распаковать данные в себя. Вернёт true, если распакованы без ошибок или ошибки исправлены 135 | static bool decode(void* src, size_t size) { 136 | if (size & (BLOCK_SIZE - 1)) return false; // не кратно размеру блока 137 | 138 | size_t bit = 0; 139 | size_t maxB = (size / BLOCK_SIZE) * BLOCK_SIZE_B; 140 | 141 | for (size_t b = 0; b < maxB; b += BLOCK_SIZE_B) { 142 | // 2. Получаем хэш ошибки и общий parity 143 | uint8_t sum = 0, count = 0; 144 | for (uint8_t i = 0; i < BLOCK_SIZE_B; i++) { 145 | if (_readB(src, b + i)) { 146 | sum ^= i; 147 | if (i) ++count; 148 | } 149 | } 150 | 151 | // 3. Анализируем результат 152 | if (sum) { // есть ошибки 153 | if (_readB(src, b) == (count & 1)) return false; // 2 и больше ошибок 154 | else _toggleB(src, b + sum); // данные восстановлены 155 | } 156 | 157 | // 4. Собираем дату из ячеек Хэмминга 158 | for (uint8_t i = 0; i < BLOCK_SIZE_B; i++) { 159 | if (i & (i - 1)) { // не степень двойки 160 | _readB(src, b + i) ? _setB(src, bit) : _clrB(src, bit); 161 | ++bit; 162 | } 163 | } 164 | } 165 | return true; 166 | } 167 | 168 | // замешать запакованные данные. false если ошибка аллокации 169 | static bool mix(void* pack, size_t size) { 170 | HM_BEGIN_MIX; 171 | _mix(buf, (uint8_t*)pack, size); 172 | HM_END_MIX; 173 | } 174 | 175 | // размешать запакованные данные. false если ошибка аллокации 176 | static bool unmix(void* pack, size_t size) { 177 | HM_BEGIN_MIX; 178 | _unmix(buf, (uint8_t*)pack, size); 179 | HM_END_MIX; 180 | } 181 | 182 | // замешать запакованные данные. false если ошибка аллокации 183 | static bool mix8(void* pack, size_t size) { 184 | HM_BEGIN_MIX; 185 | hamm::mix8(buf, (uint8_t*)pack, size); 186 | HM_END_MIX; 187 | } 188 | 189 | // размешать запакованные данные. false если ошибка аллокации 190 | static bool unmix8(void* pack, size_t size) { 191 | HM_BEGIN_MIX; 192 | hamm::unmix8(buf, (uint8_t*)pack, size); 193 | HM_END_MIX; 194 | } 195 | 196 | private: 197 | static void _mix(void* buf, void* pack, size_t size) { 198 | size_t k = 0; 199 | size_t top = (size / BLOCK_SIZE) * BLOCK_SIZE_B; 200 | for (uint8_t i = 0; i < BLOCK_SIZE_B; i++) { 201 | for (size_t jb = 0; jb < top; jb += BLOCK_SIZE_B) { 202 | if (_readB(pack, jb + i)) _setB(buf, k); 203 | ++k; 204 | } 205 | } 206 | } 207 | static void _unmix(void* buf, void* pack, size_t size) { 208 | size_t k = 0; 209 | size_t top = (size / BLOCK_SIZE) * BLOCK_SIZE_B; 210 | for (uint8_t i = 0; i < BLOCK_SIZE_B; i++) { 211 | for (size_t jb = 0; jb < top; jb += BLOCK_SIZE_B) { 212 | if (_readB(pack, k)) _setB(buf, jb + i); 213 | ++k; 214 | } 215 | } 216 | } 217 | 218 | static inline void _setB(void* buf, size_t b) __attribute__((always_inline)) { 219 | ((uint8_t*)buf)[b >> 3] |= 1 << (b & 0b111); 220 | } 221 | static inline void _clrB(void* buf, size_t b) __attribute__((always_inline)) { 222 | ((uint8_t*)buf)[b >> 3] &= ~(1 << (b & 0b111)); 223 | } 224 | static inline bool _readB(const void* buf, size_t b) __attribute__((always_inline)) { 225 | return (((const uint8_t*)buf)[b >> 3] >> (b & 0b111)) & 1; 226 | } 227 | static inline void _toggleB(void* buf, size_t b) __attribute__((always_inline)) { 228 | _readB(buf, b) ? _clrB(buf, b) : _setB(buf, b); 229 | } 230 | }; 231 | 232 | class Hamming4 : public HammingT<4> {}; 233 | class Hamming5 : public HammingT<5> {}; 234 | class Hamming6 : public HammingT<6> {}; 235 | class Hamming7 : public HammingT<7> {}; 236 | 237 | ///////////// 238 | 239 | class Hamming3 { 240 | public: 241 | static constexpr size_t BLOCK_SIZE = 1; // вес блока в байтах 242 | static constexpr size_t BLOCK_SIZE_B = 8; // вес блока в битах 243 | static constexpr size_t BLOCK_DATA_B = 4; // битов данных на блок 244 | 245 | // размер запакованных данных по размеру исходных 246 | static constexpr size_t encodedSize(size_t size) { 247 | return size * 2; 248 | } 249 | 250 | // размер распакованных данных по размеру запакованных. 0 - некорректный размер пакета 251 | static constexpr size_t decodedSize(size_t size) { 252 | return (size & 1) ? 0 : (size >> 1); 253 | } 254 | 255 | // Запаковать данные во внешний буфер dest размера encodedSize() [должен быть инициализирован 0] 256 | static void encode(void* dest, const void* src, size_t size) { 257 | encode((uint8_t*)dest, (const uint8_t*)src, size); 258 | } 259 | static void encode(uint8_t* dest, const uint8_t* src, size_t size) { 260 | while (size--) { 261 | *dest++ = encode(*src & 0xf); 262 | *dest++ = encode(*src >> 4); 263 | ++src; 264 | } 265 | } 266 | 267 | // Распаковать данные в себя. Вернёт true, если распакованы без ошибок или ошибки исправлены 268 | static bool decode(void* src, size_t size) { 269 | return decode((uint8_t*)src, size); 270 | } 271 | static bool decode(uint8_t* src, size_t size) { 272 | if (size & 1) return false; 273 | 274 | uint8_t* dest = src; 275 | while (size) { 276 | int16_t bl = decode(*src++); 277 | int16_t bh = decode(*src++); 278 | if (bl < 0 || bh < 0) return false; 279 | *dest++ = (bh << 4) | bl; 280 | size -= 2; 281 | } 282 | return true; 283 | } 284 | 285 | // замешать запакованные данные. false если ошибка аллокации 286 | static bool mix(void* pack, size_t size) { 287 | if (size & 1) return false; 288 | 289 | HM_BEGIN_MIX; 290 | _mix(buf, (uint8_t*)pack, size); 291 | HM_END_MIX; 292 | } 293 | 294 | // размешать запакованные данные. false если ошибка аллокации 295 | static bool unmix(void* pack, size_t size) { 296 | if (size & 1) return false; 297 | 298 | HM_BEGIN_MIX; 299 | _unmix(buf, (uint8_t*)pack, size); 300 | HM_END_MIX; 301 | } 302 | 303 | // замешать запакованные данные. false если ошибка аллокации 304 | static bool mix8(void* pack, size_t size) { 305 | if (size & 1) return false; 306 | 307 | HM_BEGIN_MIX; 308 | hamm::mix8(buf, (uint8_t*)pack, size); 309 | HM_END_MIX; 310 | } 311 | 312 | // размешать запакованные данные. false если ошибка аллокации 313 | static bool unmix8(void* pack, size_t size) { 314 | if (size & 1) return false; 315 | 316 | HM_BEGIN_MIX; 317 | hamm::unmix8(buf, (uint8_t*)pack, size); 318 | HM_END_MIX; 319 | } 320 | 321 | // запаковать 4 бита 322 | static uint8_t encode(uint8_t nibble) { 323 | #ifdef __AVR__ 324 | return pgm_read_byte(&hamm::table84[nibble]); 325 | #else 326 | return hamm::table84[nibble]; 327 | #endif 328 | } 329 | 330 | // распаковать 8 бит в 4 бита 331 | static int16_t decode(uint8_t data) { 332 | switch (data) { 333 | case 0x00: 334 | case 0x01: 335 | case 0x0E: 336 | case 0x0F: 337 | case 0x32: 338 | case 0x33: 339 | case 0x3C: 340 | case 0x3D: 341 | case 0x54: 342 | case 0x55: 343 | case 0x5A: 344 | case 0x5B: 345 | case 0x66: 346 | case 0x67: 347 | case 0x68: 348 | case 0x69: 349 | case 0x96: 350 | case 0x97: 351 | case 0x98: 352 | case 0x99: 353 | case 0xA4: 354 | case 0xA5: 355 | case 0xAA: 356 | case 0xAB: 357 | case 0xC2: 358 | case 0xC3: 359 | case 0xCC: 360 | case 0xCD: 361 | case 0xF0: 362 | case 0xF1: 363 | case 0xFE: 364 | case 0xFF: 365 | break; 366 | 367 | default: 368 | uint8_t sum = 0, count = 0; 369 | for (uint8_t i = 0; i < 8; i++) { 370 | if ((data >> i) & 1) { 371 | sum ^= i; 372 | if (i) ++count; 373 | } 374 | } 375 | if (sum) { 376 | if ((data & 1) == (count & 1)) return -1; 377 | else data ^= (1 << sum); 378 | } 379 | } 380 | return (data >> 5) | ((data >> 3) & 1); 381 | } 382 | 383 | private: 384 | static void _mix(void* buf, void* pack, size_t size) { 385 | size_t k = 0, top = size * 8; 386 | for (uint8_t i = 0; i < 8; i++) { 387 | for (size_t jb = 0; jb < top; jb += 8) { 388 | if (_readB(pack, jb + i)) _setB(buf, k); 389 | ++k; 390 | } 391 | } 392 | } 393 | static void _unmix(void* buf, void* pack, size_t size) { 394 | size_t k = 0, top = size * 8; 395 | for (uint8_t i = 0; i < 8; i++) { 396 | for (size_t jb = 0; jb < top; jb += 8) { 397 | if (_readB(pack, k)) _setB(buf, jb + i); 398 | ++k; 399 | } 400 | } 401 | } 402 | 403 | static inline void _setB(void* buf, size_t b) __attribute__((always_inline)) { 404 | ((uint8_t*)buf)[b >> 3] |= 1 << (b & 0b111); 405 | } 406 | 407 | static inline bool _readB(const void* buf, size_t b) __attribute__((always_inline)) { 408 | return (((const uint8_t*)buf)[b >> 3] >> (b & 0b111)) & 1; 409 | } 410 | }; -------------------------------------------------------------------------------- /src/HammingOld.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /* 5 | статусы операции (pack/unpack): 6 | 0 - ОК 7 | 1 - исправлены ошибки (unpack) 8 | 2 - есть неисправленные ошибки (unpack) 9 | 3 - parity error (unpack) 10 | 4 - битый пакет (unpack) 11 | 5 - ошибка аллокации буфера 12 | */ 13 | 14 | template // порядок алгоритма (4-7) 15 | class Hamming { 16 | public: 17 | // запаковать данные в буфер, возвращает статус операции 18 | template 19 | uint8_t pack(T& data) { 20 | return pack((uint8_t*)&data, (uint32_t)sizeof(T)); 21 | } 22 | 23 | uint8_t pack(uint8_t* ptr, uint32_t size) { 24 | // 0. Считаем и создаём буфер 25 | stat = 0; 26 | uint8_t signif = chunkSizeB - (HAM_SIZE + 1); // битов даты на чанк 27 | chunkAmount = (size * 8ul + signif - 1) / signif; // колво чанков (целоч. деление) 28 | bytes = chunkAmount * chunkSize; // размер буфера, байт 29 | if (buffer) free(buffer); // освобождаем старый 30 | buffer = (uint8_t*)malloc(bytes); // выделяем 31 | if (!buffer) return stat = 5; // не удалось создать 32 | uint8_t buf[bytes]; // ещё буфер 33 | memset(buf, 0, bytes); // чисти чисти 34 | memset(buffer, 0, bytes); // чисти чисти 35 | int ptrCount = 0; 36 | 37 | for (int chunk = 0; chunk < chunkAmount; chunk++) { // каждый чанк 38 | // 1. Заполняем дату, минуя ячейки Хэмминга (0,1,2,4,8...) 39 | for (uint8_t i = 0; i < chunkSizeB; i++) { 40 | if ((i & (i - 1)) != 0) { // проверка на степень двойки 41 | write(buf, chunk * chunkSizeB + i, read(ptr, ptrCount++)); // переписываем побитно 42 | } 43 | } 44 | 45 | // 2. Считаем и пишем parity для зон Хэмминга 46 | uint8_t parityH = 0; 47 | for (uint8_t i = 0; i < chunkSizeB; i++) { 48 | for (uint8_t j = 0; j < HAM_SIZE; j++) { 49 | // если это ячейка хэмминга и бит стоит, инвертируем текущий parity 50 | if ((i & (1 << j)) && read(buf, chunk * chunkSizeB + i)) parityH ^= (1 << j); 51 | } 52 | } 53 | for (uint8_t i = 0; i < HAM_SIZE; i++) { 54 | write(buf, chunk * chunkSizeB + (1 << i), (parityH >> i) & 1); // переписываем parity ячеек хэмминга 55 | } 56 | 57 | // 3. Считаем и пишем общий parity 58 | uint8_t count = 0; 59 | for (uint8_t i = 1; i < chunkSizeB; i++) { 60 | if (read(buf, chunk * chunkSizeB + i)) count++; // считаем 61 | } 62 | write(buf, chunk * chunkSizeB, count & 1); // пишем 63 | } 64 | 65 | // 4. Перемешиваем 66 | uint32_t k = 0; 67 | for (uint8_t i = 0; i < chunkSizeB; i++) { 68 | for (uint8_t j = 0; j < chunkAmount; j++) { 69 | write(buffer, k++, read(buf, i + j * chunkSizeB)); 70 | } 71 | } 72 | return stat; 73 | } 74 | 75 | // распаковать данные, возвращает статус операции 76 | uint8_t unpack(uint8_t* data, uint32_t size) { 77 | // 0. Считаем и создаём буфер 78 | stat = 0; 79 | if ((size & (chunkSize - 1)) != 0) return stat = 4; // не кратно размеру чанка 80 | uint8_t signif = chunkSizeB - (HAM_SIZE + 1); // битов даты на чанк 81 | chunkAmount = (uint32_t)size / chunkSize; // колво чанков 82 | bytes = chunkAmount * signif / 8; // размер буфера, байт (округл. вниз) 83 | if (buffer) free(buffer); // чисти старый 84 | buffer = (uint8_t*)malloc(bytes); // выделяем 85 | if (!buffer) return stat = 5; // не удалось создать 86 | memset(buffer, 0, bytes); // чисти чисти 87 | uint8_t buf[size]; 88 | int ptrCount = 0; 89 | 90 | // 1. Разбираем мешанину обратно 91 | uint32_t k = 0; 92 | for (uint8_t i = 0; i < chunkSizeB; i++) { 93 | for (uint8_t j = 0; j < chunkAmount; j++) { 94 | write(buf, i + j * chunkSizeB, read(data, k++)); 95 | } 96 | } 97 | 98 | for (int chunk = 0; chunk < chunkAmount; chunk++) { // каждый чанк 99 | // 2. Получаем хэш ошибки и общий parity 100 | uint8_t sum = 0, count = 0; 101 | for (uint8_t i = 0; i < chunkSizeB; i++) { 102 | if (read(buf, chunk * chunkSizeB + i)) { 103 | sum ^= i; 104 | if (i > 0) count++; 105 | } 106 | } 107 | 108 | // 3. Анализируем результат 109 | if (sum != 0) { // есть ошибки 110 | if (read(buf, chunk * chunkSizeB) == (count & 1)) stat = max(stat, 2); // 2 и больше ошибок 111 | else toggle(buf, chunk * chunkSizeB + sum); // данные восстановлены 112 | stat = max(stat, 1); 113 | } else { 114 | if (read(buf, chunk * chunkSizeB) != (count & 1)) stat = max(stat, 3); // parity error 115 | } 116 | 117 | // 4. Собираем дату из ячеек Хэмминга 118 | for (uint8_t i = 0; i < chunkSizeB; i++) { 119 | if ((i & (i - 1)) != 0) { // проверка на степень двойки 120 | write(buffer, ptrCount++, read(buf, chunk * chunkSizeB + i)); // переписываем побитно 121 | } 122 | } 123 | } 124 | return stat; 125 | } 126 | 127 | // возвращает статус последней операции 128 | uint8_t status() { 129 | return stat; 130 | } 131 | 132 | // размер буфера (больше чем размер входных данных) 133 | uint32_t length() { 134 | return bytes; 135 | } 136 | 137 | // деструктор 138 | ~Hamming() { 139 | stop(); 140 | } 141 | 142 | // освободить буфер 143 | void stop() { 144 | if (buffer) free(buffer); 145 | } 146 | 147 | // внутренний буфер 148 | uint8_t* buffer = NULL; 149 | 150 | private: 151 | void set(uint8_t* buf, uint32_t num) { 152 | bitSet(buf[num >> 3], num & 0b111); 153 | } 154 | void clear(uint8_t* buf, uint32_t num) { 155 | bitClear(buf[num >> 3], num & 0b111); 156 | } 157 | void write(uint8_t* buf, uint32_t num, bool state) { 158 | state ? set(buf, num) : clear(buf, num); 159 | } 160 | bool read(uint8_t* buf, uint32_t num) { 161 | return bitRead(buf[num >> 3], num & 0b111); 162 | } 163 | void toggle(uint8_t* buf, uint32_t num) { 164 | read(buf, num) ? clear(buf, num) : set(buf, num); 165 | } 166 | int stat; 167 | uint32_t bytes = 0; 168 | uint32_t chunkAmount = 0; 169 | const uint8_t chunkSizeB = (1 << HAM_SIZE); // вес чанка в битах 170 | const uint8_t chunkSize = (1 << HAM_SIZE) >> 3; // вес чанка в байтах 171 | }; 172 | --------------------------------------------------------------------------------