W moich badaniach opracowuję narzędzie do wysyłania dowolnych danych za pośrednictwem urządzenia komunikacyjnego radiowego, który jest połączony szeregiem. PYSERIAL jest używany do komunikacji

Każda polecenie wysyłane do urządzenia ma strukturę jest otoczony w starcie / StopByte, jeśli nasz ładunek jest np. Dane, wygląda jak

cmd = b'\x02' + DATA.encode() + b'\x03'

Dane mogą być duże, a komunikacja jest bardzo powolna, więc próbuję użyć zlib do kompresji.

from zlib import compress, decompress
DATA_comp = compress(DATA.encode())
cmd = b'\x02' + DATA_comp + b'\x03'

Ale Kompresja może wprowadzić znaki b 'x02' i B 'x03' gdzieś w ładunku. Prowadzi to do błędów, ponieważ oprogramowanie układowe urządzenia traktuje te bajty sterujące!

Czy istnieje sposób na określenie Zlib (lub dowolnej innej metody kompresji), aby nie używać kilku bajtów w sprężonym wyjściu?

TL; Dr: Kompresja wprowadza bajty sterujące do ładunku, które nie są trakatem przez urządzenie

0
Suthep 2 lipiec 2017, 10:52

3 odpowiedzi

Najlepsza odpowiedź

Możemy złamać problem na dwie części:

  1. Kompresuj dane.
  2. Przekształcić skompresowane dane, które nie zawiera bajtu 3.

Po drugiej części możesz użyć wielu kodowania. Na przykład Base64 kodowanie nie emituje bajtu 3. Robiąc to nieco dalej, można użyć Base255 kodowania z ważnymi symbolami 0-2 i 4-255.

1
John Zwinck 2 lipiec 2017, 08:03

Jak zauważono, niech Zlib wykonuje swoją rzecz, a następnie zakodować wynikowy strumień bitów, aby uniknąć zabronionych bajtów. Można to zrobić skutecznie i szybko, co oznaczają wyrządzenia DECODOWANIE Huffmana strumienia bitów do liczby symboli mniejszych niż 256, które są pożądane. Następnie kodując ten strumień symboli na drugim końcu za pomocą kodu Huffmana, konwertując go z powrotem do oryginalnego strumienia bitów.

Dla niewielkiej liczby bajtów, aby uniknąć, pociągnąłbyś 7 bitów ze strumienia. W zależności od wartości 7 bitów, albo ciągnij lub nie wyciągaj więcej. Mapuj 7 lub 8 bitów do pożądanego podzbioru bajtów. Powtarzać. Rozważ zero bitów do dołączenia do końca wejścia, aby umożliwić użycie wszystkich bitów wejściowych. Odwróć, aby przywrócić, wyrzucić mniej niż 8 zerowych bitów wyprodukowanych na końcu.

Oto przykład kod:

/*
  avoid.c version 1.0, 2 July 2017

  Copyright (C) 2017 Mark Adler

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.

  Mark Adler
  madler@alumni.caltech.edu
*/

// Take arbitrary binary input and encode it to avoid specified byte values.
// The number of such values to avoid is the parameter "cut". The input is
// taken as a stream of bits. At each step either 7 or 8 bits of input is coded
// to an output byte. As a result, the input bits are expanded by a factor
// between 1 and about 1.143 (rounded up to the next multiple of 8 bits),
// depending on the value of cut and depending on the input data. cut must be
// in the range 1..128. For random input, the average expansion ratio is
// 1/(1-cut/1024).
//
// avoid() does the encoding, and restore() does the decodng. avoid() uses the
// table map[], which maps the values 0..255-cut to the allowed byte values,
// i.e. the byte values that are not cut. invert_map() is provided to invert
// that transformation to make the table unmap[], which is used by restore().

#include <stddef.h>

// Encode input[0..len-1] into a subset of permitted byte values, which number
// cut less than 256. Therefore cut values are cut from the set of possible
// output byte values. map[0..255-cut] is the set of allowed byte values. cut
// must be in the range 1..128. If cut is out of range, zero is returned and no
// encoding is performed. Otherwise the return value is the size of the encoded
// result. size is the size of the output space in bytes, which should be at
// least the maximum possible encoded size, equal to ceiling(len * 8 / 7). The
// return value may be larger than size, in which case only size bytes are
// written to *out, with the remaining encoded data lost. Otherwise the number
// of bytes written to *out is the returned value.
size_t avoid(unsigned char *output, size_t size,
             unsigned char const *input, size_t len,
             unsigned char const *map, unsigned cut) {
    if (len == 0 || cut < 1 || cut > 128)
        return 0;
    unsigned buf = *input, code = buf;
    int bits = 8;
    size_t in = 1, out = 0;
    for (;;) {
        unsigned less = code >> 1;
        if (less < cut) {
            code = less;
            bits -= 7;
        }
        else {
            code -= cut;
            bits -= 8;
        }
        if (out < size)
            output[out] = map[code];
        out++;
        if (in == len && bits <= 0)
            return out;
        if (in < len) {
            if (bits < 8) {
                buf = (buf << 8) + input[in++];
                bits += 8;
            }
            code = buf >> (bits - 8);
        }
        else
            code = buf << (8 - bits);   // pad with zeros
        code &= 0xff;
    }
}

// Invert the map used by avoid() for use by restore().
void invert_map(unsigned char *unmap, unsigned char const *map, unsigned cut) {
    if (cut < 1 || cut > 128)
        return;
    unsigned k = 0;
    do {
        unmap[k++] = 255;
    } while (k < 256);
    k -= cut;
    do {
        k--;
        unmap[map[k]] = k;
    } while (k);
}

// Restore the data input[0..len-1] that was encoded with avoid(), writing the
// restored bytes to *output. The number of restored bytes is returned. size is
// the size of the output space in bytes, which should be at least the maximum
// possible restored size, equal to len. If the returned value is greater than
// size, then only size bytes are written to *output, with the remainder of the
// restored data lost. unmap[k] gives the corresponding code for character k in
// the range 0..255-cut if k is in the allowed set, or 255 if k is not in the
// allowed set. Characters in the input that are not in the allowed set are
// ignored. cut must be in the range 1..128. If cut is out of range, zero is
// returned and no restoration is conducted.
size_t restore(unsigned char *output, size_t size,
               unsigned char const *input, size_t len,
               unsigned char const *unmap, unsigned cut) {
    if (cut < 1 || cut > 128)
        return 0;
    unsigned buf = 0;
    int bits = 0;
    size_t in = 0, out = 0;
    while (in < len) {
        unsigned code = unmap[input[in++]];
        if (code == 255)
            continue;
        if (code < cut) {
            buf <<= 7;
            bits += 7;
        }
        else {
            buf <<= 8;
            bits += 8;
            buf += cut;
        }
        buf += code;
        if (bits >= 8) {
            if (out < size)
                output[out] = buf >> (bits - 8);
            out++;
            bits -= 8;
        }
    }
    return out;
}
0
Mark Adler 3 lipiec 2017, 07:25

Z pomocą @johnzwinck przyszedłem do następnego wniosku (przedstawiony w minimalnym przykładzie roboczym)

from zlib import compress, decompress
from base64 import b64encode
DATA_comp = compress(DATA.encode())
DATA_enc = b64encode(DATA_comp)
cmd = b'\x02' + DATA_enc + b'\x03'

Po stronie odbiorczej jest odwrotnie.

Jak wskazano @ Błotosmędek, rozmiar ładunku jest ponownie skreślony przez stały czynnik. Korzystanie z ASCII85 może być lepsze.

0
Suthep 2 lipiec 2017, 08:27