J'ai eu l'opportunité de traiter les données binaires avec Python, mais comme j'avais touché aux données binaires pour la première fois et que j'avais dû enquêter sur diverses choses, j'aimerais les garder en mémoire!
Cette fois, nous avons traité les données du fichier d'extension "**. Sl2 **", nous allons donc écrire les données .sl2 à titre d'exemple!
La structure de données des données binaires est déterminée par chaque format. Cette fois, j'avais affaire à un fichier appelé .sl2 que j'ai vu pour la première fois, mais dans ce cas, j'ai d'abord besoin de connaître "la structure du fichier .sl2" d'une manière ou d'une autre. Si vous ne le savez pas, vous ne pouvez pas le gérer!
Dans mon cas, je me suis référé à la page suivante, donc je vais expliquer en fonction de cette page de référence. (** En conséquence, cette page était fausse ... **) https://wiki.openstreetmap.org/wiki/SL2
Il semble que la plupart des données binaires aient un en-tête. Il s'agit d'une valeur fixe dans les premiers octets et contient une description du format de données telle que des informations de version.
Dans ce cas, le tableau et l'explication suivants sont décrits dans la colonne "** Structure de base **" de la page de référence. Il semble qu'il existe plusieurs types dans le fichier .sl2, mais pour le moment, il semble que l'en-tête soit de 10 octets.
The files show up with a 10 byte header. First 2 bytes describe the format version (01=SLG, 02=SL2). Bytes 5,4 provide the block size. It varies depending on the sensor: 0x07b2 for HDI=Primary/Secondary+DSI and 0x0c80 for Sidescan). Seen values are
Très grosso modo, il s'agit de l '«arrangement» et de l' «ordre d'arrangement» des données et définit l'ordre dans lequel les données sont stockées lorsqu'elles sont écrites en mémoire.
Pour autant que je sache, l'ordre des octets appartient le plus souvent à "Big Endian" ou "Little Endian", vous devez donc savoir lequel.
Dans ce cas, j'ai découvert que c'était un peu tolandien car il y a la description suivante dans la colonne "** Structure de base **" de la page de référence.
The file is a binary file with little endian format. Float values are stored as IEEE 754 floating-point "single format".
Enfin, nous lirons les données après l'en-tête. Sur la page de référence, le type de données et la longueur de chaque bloc sont définis comme indiqué dans le tableau ci-dessous (extrait partiel).
Tout d'abord, regardez la colonne de description des données dans la colonne la plus à droite et sélectionnez les données que vous souhaitez extraire. Après avoir décidé des données à extraire, vérifiez le type de données (type de variable) et la valeur de décalage.
Le décalage est l'information de la position par rapport au point de référence et représente l'adresse des données. Puisqu'il y a 144 octets dans un ensemble de données cette fois, cela signifie que le nombre d'octets dans lesquels les données sont écrites est affiché.
This should close the 144 byte frame.
J'ai beaucoup organisé sur les données binaires ci-dessus. Nous utilisons un module appelé ** struct ** pour gérer ces données binaires. Document officiel
La documentation officielle contient le tableau suivant, qui définit les caractères qui représentent l'ordre des octets.
Comme confirmé dans le chapitre [Byte Block](#Byte Block), chaque donnée a son propre type de données (type valable).
Il est nécessaire de changer la méthode de traitement en fonction du type de données, mais dans struct
, tant que vous passez les informations de type de données
Après cela, on a l'impression qu'il fera tout ce qu'il faut pour s'adapter au moule.
Cependant, le format peut différer du document officiel, vous devez donc le lire en conséquence. Dans ce cas, ce sera comme suit.
short int → unsigned short(H)
int → unsigned long(L)
byte(int) → unsigned char(B)
Il peut être lu en définissant l'option lors de l'ouverture du fichier sur rb (read binary)
.
with open(file_name, 'br') as f:
data = f.read()
Vous pouvez convertir les données binaires lues en utilisant la fonction struct.unpack_from ()
.
Le format de base est struct.unpack_from (type de données, données, offset)
.
Je connais déjà le type de données et l'offset, il ne me reste plus qu'à le spécifier!
Voici une vue d'ensemble. Cela semble plus long que ce à quoi je m'attendais, mais en gros, j'ajuste d'abord l'en-tête, puis je répète le travail de déballage en décalant le décalage du nombre d'éléments.
import sys
import struct
OLAR_EARTH_RADIUS = 6356752.3142
# PI = Math: : PI
MAX_UINT4 = 4294967295
FT2M = 1/3.2808399 # factor for feet to meter conversions
KN2KM = 1/1.852 # factor for knots to km conversions
args = sys.argv
if args[1] == '':
print('Usage: python sl2decoder.py your_file.sl2')
block_offset = 0
#Décaler d'en-tête de 10 octets
block_offset += 10
# Datatypes:
# ===================================================================================================
# Type Definition Directive for Python's String#unpack
# ---------------------------------------------------------------------------------------------------
# byte UInt8 B
# short UInt16LE H
# int UInt32LE L
# float FloatLE (32 bits IEEE 754 floating point number) f
# flags UInt16LE H
# ---------------------------------------------------------------------------------------------------
#Définir le décalage et le type de données pour chaque élément
block_def = {
'blockSize' : {'offset': 26, 'type': '<H'},
# 'lastBlockSize': {'offset': 28, 'type': '<H'},
'channel' : {'offset': 30, 'type': '<H'},
'packetSize' : {'offset': 32, 'type': '<H'},
'frameIndex' : {'offset': 34, 'type': '<L'},
'upperLimit' : {'offset': 38, 'type': '<f'},
'lowerLimit' : {'offset': 42, 'type': '<f'},
'frequency' : {'offset': 51, 'type': '<B'},
# 'time1': {'offset': 58, 'type': '<H'} # unknown resolution, unknown epoche
'waterDepthFt' : {'offset': 62, 'type': '<f'}, # in feet
'keelDepthFt' : {'offset': 66, 'type': '<f'}, # in feet
'speedGpsKnots' : {'offset': 98, 'type': '<f'}, # in knots
'temperature' : {'offset': 102, 'type': '<f'}, # in °C
'lowrance_longitude': {'offset': 106, 'type': '<L'}, # Lowrance encoding (easting)
'lowrance_latitude' : {'offset': 110, 'type': '<L'}, # Lowrance encoding (northing)
'speedWaterKnots' : {'offset': 114, 'type': '<f'}, # from "water wheel sensor" if present, else GPS value(?)
'courseOverGround' : {'offset': 118, 'type': '<f'}, # ourseOverGround in radians
'altitudeFt' : {'offset': 122, 'type': '<f'}, # in feet
'heading' : {'offset': 126, 'type': '<f'}, # in radians
'flags' : {'offset': 130, 'type': '<H'},
# 'time': {'offset': 138, 'type': '<H', 'len': 4} # unknown resolution, unknown epoche
}
with open('%s_output_py.csv' % args[0], 'w') as f_raw:
title = ','.join(['Channel', 'Frequency', 'UpperLimit[ft]', 'LowerLimit[ft]', 'Depth[ft]', 'WaterTemp[C]', 'WaterSpeed[kn]',
'PositionX', 'PositionY', 'Speed[kn]', 'Track[rad]','Altitude[ft]', 'Heading[rad]']) + '\n'
f_raw.write(title)
alive_counter = 0
with open(args[1], 'br') as f:
data = f.read()
sl2_file_size = len(data)
while block_offset < sl2_file_size:
h = {}
if alive_counter % 100 == 0:
print('%d done...' % round(100.0*block_offset/sl2_file_size))
for k, v in block_def.items():
t_offset = block_offset + v['offset']
h[k] = struct.unpack_from(v['type'], data, t_offset)
print(h['blockSize'])
block_offset += h['blockSize'][0]
#Combinez en une seule ligne de données
csv_line = ','.join([str(h['channel'][0]), str(h['frequency'][0]),
str(h['upperLimit'][0]), str(h['lowerLimit'][0]),
str(h['waterDepthFt'][0]), str(h['temperature'][0]),
str(h['speedWaterKnots'][0]), str(h['lowrance_longitude'][0]),
str(h['lowrance_latitude'][0]), str(h['speedGpsKnots'][0]),
str(h['courseOverGround'][0]), str(h['altitudeFt'][0]),
str(h['heading'][0])]) + '\n'
f_raw.write(csv_line)
print('Read up to block_offset %d' % block_offset)
Recommended Posts