Compresión de datos para una gran cantidad de datos binarios

2

En una aplicación mía integrada, el dispositivo genera una gran cantidad de datos de instrumentación que se almacenan cada pocos minutos en un NVM, como un conjunto de EEPROM o Data Flash. El personal de campo descarga estos datos desde el dispositivo mediante una conexión serial lenta (RS-232) a una velocidad de 9600 bps. Solo para darle una idea, los datos de una instancia son 64 bytes, almacenados cada 15 minutos, durante 180 días. Con los gastos generales del protocolo, esto lleva más de 25-30 minutos para descargar, lo que es una molestia. Los datos luego se analizan en una estación central en sistemas de calidad de PC. Además, el sistema es extremadamente sensible a los costos, y cualquier ahorro en los costos de NVM es bienvenido.

Estaba buscando algunos esquemas de compresión razonables que puedo usar para comprimir los datos durante el almacenamiento. El esquema que estaba pensando en apagar era almacenar cierta cantidad de instancias en un dispositivo NVM más pequeño y comprimirlos de una vez después de que el búfer estuviera lleno. Intenté usar una implementación del algoritmo LZW, pero no pude pasar del 75% de compresión. Una muestra de los datos se reproduce a continuación. Estos son valores hexadecimales. (Cada instancia tiene solo 32 bytes en esta muestra).

00 12 23 09 12 5A 69 5B 4E 5B 96 00 3B 00 21 F7
00 44 03 2F 03 B6 01 D6 00 00 5A 4B 58 00 00 FD
15 12 23 09 12 5A 0E 5A F2 5B 19 00 3B 00 24 14
00 50 03 66 03 F7 02 08 00 00 5A 4D 51 00 00 4B
30 12 23 09 12 59 DF 5B 71 5A AA 00 3C 00 27 15
00 67 03 D9 04 9C 02 7B 00 00 59 4D 53 00 00 A7
45 12 23 09 12 59 AF 5B 61 5A 97 00 3D 00 27 52
00 54 03 93 04 38 02 3A 00 00 59 4E 52 00 00 A5
00 13 23 09 12 5A 08 5B A8 5A DF 00 3B 00 27 AF
00 50 03 61 03 F2 02 03 00 00 58 4E 56 00 00 56
15 13 23 09 12 5A 7E 5C 67 5B 95 00 3B 00 28 AC
00 5B 03 A2 04 56 02 53 00 00 57 4D 50 00 00 5D
30 13 23 09 12 5B 72 5C CE 5C 35 00 3B 00 28 94
00 63 03 CA 04 A1 02 A3 00 00 57 4B 4F 00 00 95
45 13 23 09 12 5B 41 5C 3E 5C 45 00 3B 00 28 30
00 51 03 84 04 2E 02 3F 00 00 58 4C 50 00 00 C1
00 14 23 09 12 5A E8 5B ED 5B ED 00 3C 00 28 78
00 4D 03 6B 04 15 02 35 00 00 56 4D 48 00 00 0A
15 14 23 09 12 5A FA 5B EA 5B 95 00 3D 00 27 AC
00 67 03 DE 04 B5 02 A3 00 00 58 4E 49 00 00 6B
30 14 23 09 12 5A 2B 5A FD 5A F2 00 40 00 27 EF
00 5B 03 B6 04 6A 02 5D 00 00 5A 4E 50 00 00 27
45 14 23 09 12 5A 9D 5B 69 5B 43 00 23 00 16 D7
00 43 02 8F 02 FD 01 7C 00 00 62 59 56 00 00 9F
00 15 23 09 12 5B 6F 5C 2F 5B DC 00 0B 00 06 10
00 4A 01 C7 02 1C 01 18 00 00 5C 50 4F 00 00 BC
15 15 23 09 12 5B E0 5C E9 5C 37 00 0D 00 05 73
00 32 01 6D 01 A4 00 C8 00 00 64 44 3D 00 00 0E
30 15 23 09 12 5B F1 5C C1 5C 36 00 0D 00 04 71
00 30 01 59 01 8B 00 AF 00 00 57 4D 55 00 00 42
45 15 23 09 12 5B E7 5D 04 5C 3C 00 09 00 01 23
00 30 01 36 01 68 00 A5 00 00 5B 4F 56 00 00 8B
00 16 23 09 12 5C 5E 5D 57 5C A8 00 0B 00 01 2E
00 27 01 0E 01 3B 00 91 00 00 62 4F 56 00 00 F6
15 16 23 09 12 5C C1 5D AF 5C D8 00 0D 00 01 2C
00 31 01 4F 01 81 00 B4 00 00 62 48 55 00 00 4A
30 16 23 09 12 5C B2 5D 7D 5C F6 00 0C 00 02 34
00 27 01 1D 01 4A 00 9B 00 00 64 4A 3B 00 00 EC
45 16 23 09 12 5D 2D 5D 68 5D 23 00 08 00 02 8E
00 2E 01 22 01 54 00 A0 00 00 62 49 56 00 00 B9
00 17 23 09 12 5C C4 5D 76 5C C3 00 08 00 02 8F
00 22 00 F0 01 13 00 7D 00 00 59 49 57 00 00 64
15 17 23 09 12 5C E5 5D C8 5D 1B 00 08 00 02 AE
00 27 01 09 01 2C 00 87 00 00 64 4A 40 00 00 2D
30 17 23 09 12 5D 1A 5D F2 5D 0E 00 09 00 02 3F
00 28 01 0E 01 31 00 87 00 00 64 4A 57 00 00 0B
45 17 23 09 12 5D 2C 5E 09 5D 65 00 09 00 02 A9
00 25 01 09 01 2C 00 82 00 00 5C 49 40 00 00 3D
00 18 23 09 12 5D 1B 5D 4B 5D 4A 00 09 00 02 D8
00 28 01 13 01 3B 00 8C 00 00 64 4A 58 00 00 F6
15 18 23 09 12 5D 2B 5D 96 5D 97 00 09 00 02 1B
00 22 00 F0 01 13 00 78 00 00 62 4A 57 00 00 5F
30 18 23 09 12 5B C6 5B F5 5B EE 00 09 00 00 B7
00 2B 01 18 01 3B 00 8C 00 00 61 00 58 00 00 3B
45 18 23 09 12 5A E1 5B 10 5A EF 00 09 00 00 6D
00 33 01 3B 01 5E 00 96 00 00 61 00 58 00 00 E3
00 19 23 09 12 5A F3 5B 1E 5B 23 00 09 00 00 5C
00 29 01 0E 01 2C 00 7D 00 00 62 00 58 00 00 64
15 19 23 09 12 5A AC 5A DF 5A E3 00 0A 00 00 0E
00 28 01 13 01 2C 00 78 00 00 61 00 3C 00 00 82
30 19 23 09 12 5A 9C 5A B2 5A CC 00 0A 00 00 47
00 2A 01 09 01 1D 00 69 00 00 62 00 5B 00 00 88
45 19 23 09 12 5A BA 5A DB 5A EA 00 0A 00 00 CD
00 27 01 09 01 22 00 69 00 00 62 00 5A 00 00 87
00 20 23 09 12 5A EA 5B 0E 5B 41 00 0A 00 00 4F
00 1D 00 DC 00 EB 00 55 00 00 62 00 5B 00 00 0A
15 20 23 09 12 5B 5D 5B C0 5B B8 00 0A 00 00 9D
00 27 01 09 01 22 00 6E 00 00 61 00 5B 00 00 82
30 20 23 09 12 5B C0 5B AB 5B EF 00 0A 00 00 FD
00 26 01 0E 01 22 00 73 00 00 62 00 5A 00 00 79
45 20 23 09 12 5B E0 5B FC 5B F8 00 0A 00 00 6E
00 27 01 09 01 22 00 73 00 00 61 00 59 00 00 7F
00 21 23 09 12 5C 30 5C 65 5C 6E 00 0A 00 00 80
00 1D 00 D7 00 F0 00 5F 00 00 61 00 5A 00 00 02
15 21 23 09 12 5C 55 5C 93 5C 9B 00 0A 00 00 EB
00 1D 00 E1 00 F0 00 55 00 00 61 00 5A 00 00 02
30 21 23 09 12 5C EE 5D 4A 5C FE 00 0A 00 00 1C
00 23 01 04 01 1D 00 69 00 00 61 00 9D 00 00 53

¿Alguien puede indicarme un algoritmo adecuado para comprimir los datos anteriores? El algoritmo debe tener una huella de memoria pequeña (RAM) (< 5kBytes o menos).

La implementación que probé es de aquí .

EDIT 1: Acabo de tener una epifanía, donde alineé cada registro de 32/64 bytes uno debajo del otro y lo crucé verticalmente. De esta manera, los datos se volvieron mucho más repetitivos y ahora estoy obteniendo tasas de compresión mucho mejores.

Sin embargo, todavía estoy abierto a otros enfoques.

    
pregunta Vaibhav Garg

4 respuestas

5

Tienes alrededor de 1 Mbyte de datos para almacenar. La EEPROM es barata en el esquema de las cosas. La parte cara está esperando 20 minutos para capturar los datos. La respuesta obvia es aumentar la velocidad de transmisión.

A 115.2 kBaud, la transferencia tomaría 12 veces menos, o aproximadamente 2 minutos después de tener en cuenta la sobrecarga del protocolo. Entonces, arregle el firmware para utilizar una velocidad de transmisión razonable.

    
respondido por el Olin Lathrop
3

Por lo que puedo decir, los primeros bytes son una marca de fecha, y los campos restantes tienen valores que son algo similares entre los registros.

Hemos tenido que lidiar con el mismo problema al enviar datos a través de una conexión satelital costosa. Algunas formas en que redujimos la cantidad de bytes fueron:

Una marca de tiempo : en lugar de sellar la hora de cada registro, tenga una marca de tiempo para los datos para X número de registros. Usted sabe que su frecuencia de muestreo es fija, por lo que sabe cuáles serán los tiempos de los próximos registros. Lo hacemos cada X número de registros, en caso de que haya un error en el que no se haya guardado un registro o que el satélite haya perdido un paquete. por ejemplo

timestamp1,A1,B1,C1
timestamp2,A3,B2,C2
timestamp3,A3,B3,C3

se convierte en

timestamp1,A1,B1,C1,A2,B2,C2,A3,B3,C3

Deltas : si las mediciones no cambian mucho entre muestras, es posible que en su lugar se envíen las diferencias entre las mediciones. por ejemplo

timestamp1,A1,B1,C1,(A2-A1),(B2-B1),(C2-C1),(A3-A2),(B3-B2),(C3-C2)

Las diferencias pueden expresarse con menos bytes que los valores absolutos, ahorrando espacio. Por supuesto, es posible que una de las diferencias sea demasiado grande entonces. En este caso, es posible que haya datos dañados, pero esa es otra razón para limitar el número de registros antes de repetir el proceso con una nueva marca de tiempo y nuevos deltas.

Algo completamente diferente : en otro proyecto hicimos algo completamente diferente. En lugar de descargar los datos, adjuntamos un registrador de serie al puerto serie y conseguimos que el instrumento le enviara cada registro. La recuperación de los datos implicaba entonces extraer la tarjeta SD y guardar el archivo en el disco. También agregamos un botón para presionar antes de retirar el disco, para detener la escritura en la tarjeta mientras se retiraba.

    
respondido por el geometrikal
1

¿Qué hay de ver el problema en un nivel superior?
Los datos que presenta son muy simples, parece difícil encontrar patrones que puedan usarse para minimizar los requisitos de almacenamiento / tránsito. Sería útil tener una vista de los datos de nivel superior y qué información se pretende recuperar (en lugar de qué binario está almacenado actualmente).

Por ejemplo, tuve un problema similar (instrumentación, cada 15 minutos, guardado en EEPROM, conexión lenta, etc.) y mi solución se aprobó al utilizar el contexto de los datos: si faltaban muestras a tiempo (mis datos tenían la marca de tiempo) Asumiría que todos los valores faltantes son los mismos que el último registrado antes de los faltantes.
Ese tipo de enfoque podría llevarlo a una liga diferente a la de simplemente comprimir datos a la LZW.

    
respondido por el Rodrigo Lopez
1

Según lo sugerido por geometrikal, extraiga la marca de tiempo y envíelo solo una vez y use la codificación delta. Luego agrupe los campos en aquellos que "siempre" tienen un delta cero, aquellos que típicamente tienen deltas muy pequeños (por ejemplo, magnitud menor que cuatro), aquellos que típicamente tienen deltas pequeños (por ejemplo, magnitud menor a 16), y los que son mas variable

Para el grupo delta cero "siempre", use una codificación de longitud de ejecución en el grupo codificado verticalmente / a través del tiempo (un recuento de 16 bits podría ser conveniente, aunque sería suficiente con 15 bits). Si las desviaciones en este grupo son típicamente singulares (por lo que dos deltas secuenciales serían no nulos e idénticos en magnitud y opuestos en signo), el valor predeterminado (posiblemente una constante dura, posiblemente transmitida al comienzo de cada conjunto de datos) el salto de grupo se debe asumir con la siguiente longitud de ejecución utilizando el valor predeterminado. Es decir, este grupo se codificaría opcional_starting_default_value, longitud de ejecución del valor predeterminado, excepcional_valor, longitud de ejecución del valor predeterminado (tenga en cuenta que uno puede tener una longitud de ejecución de cero si dos valores excepcionales ocurren adyacentes entre sí). Si las desviaciones vienen en ráfagas que son altamente variables, puede ser conveniente utilizar una longitud de ejecución para describir la longitud de la ráfaga, cuyo valor de longitud sería seguido por la secuencia de valores delta de variante. Si las desviaciones no son extremadamente raras o no es deseable que las condiciones excepcionales exploten la demanda de ancho de banda, la longitud de ejecución que codifica los campos individuales en lugar de todo el grupo de "siempre" cero delta probablemente sería más deseable.

Para los grupos delta pequeños y muy pequeños, puede usar una codificación Huffman de secuencias de los valores delta pequeños con un código de escape. El tamaño de la tabla y cada entrada en la tabla se puede ajustar cambiando la longitud de la secuencia. Por ejemplo, la muy pequeña tabla delta (-3 a 3) con una longitud de secuencia de cuatro tendría 32 entradas y el tamaño de cada entrada podría ser de 32 bits (puede haber un campo de longitud de cinco bits con hasta 27 bits para la codificación) ; con una longitud de secuencia de ocho, la tabla tendría 64 entradas y el tamaño de cada entrada podría ser de 64 bits. Alternativamente, puede usar la secuencia delta delta / código de escape para evitar por completo la necesidad de una tabla. Cualquier valor delta alto (correspondiente a los códigos de escape) podría enumerarse en forma no codificada después del valor codificado. (Si pretende utilizar LZW después de este paso de codificación, es probable que no haya mucho beneficio en la codificación de la secuencia de Huffman ya que LZW usa un paso de codificación de Huffman. Sería deseable mantener juntos los valores delta que varían poco, de modo que las sustituciones sean más comunes dado que no hay preocupación por el tamaño de la tabla. .)

Para los campos más variables, si se va a usar un pase LZW posterior, el uso de los valores delta probablemente sea lo suficientemente bueno. La codificación Huffman de estos campos puede requerir una tabla excesivamente grande.

Como anotó en su edición, cada campo debe ejecutarse siempre que sea práctico si está utilizando un pase LZW. (Los campos delta cero "siempre" se pueden agrupar para disminuir trivialmente el tamaño: un conteo de longitud de ejecución para todo el conjunto de datos cuando ninguno de los valores cambia versus un conteo de longitud de ejecución para cada campo, pero probablemente no una optimización que vale la pena.)

    
respondido por el Paul A. Clayton

Lea otras preguntas en las etiquetas