Ich hatte die Möglichkeit, die Binärdaten mit Python zu verarbeiten, aber da ich die Binärdaten zum ersten Mal berührt hatte und verschiedene Dinge untersuchen musste, möchte ich sie als Memorandum behalten!
Dieses Mal haben wir uns mit den Daten der Erweiterungsdatei "**. Sl2 **" befasst, also werden wir die .sl2-Daten als Beispiel schreiben!
Die Datenstruktur von Binärdaten wird durch jedes Format bestimmt. Dieses Mal hatte ich es mit einer Datei namens .sl2 zu tun, die ich zum ersten Mal gesehen habe, aber in diesem Fall muss ich zuerst "die Struktur der .sl2-Datei" in irgendeiner Weise kennen. Wenn Sie das nicht wissen, können Sie nicht damit umgehen!
In meinem Fall habe ich auf die folgende Seite verwiesen, daher werde ich dies anhand dieser Referenzseite erläutern. (** Infolgedessen war diese Seite falsch ... **) https://wiki.openstreetmap.org/wiki/SL2
Es scheint, dass die meisten Binärdaten einen Header haben. Dies ist ein fester Wert in den ersten Bytes und enthält eine Beschreibung des Datenformats, z. B. Versionsinformationen.
In diesem Fall werden die folgende Tabelle und Erläuterung in der Spalte "** Grundstruktur **" der Referenzseite beschrieben. Es scheint mehrere Typen in der SL2-Datei zu geben, aber vorerst scheint der Header 10 Bytes zu sein.
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
Sehr grob gesagt ist es die "Anordnung" oder "Anordnungsreihenfolge" der Daten, die die Reihenfolge definiert, in der die Daten gespeichert werden, wenn sie in den Speicher geschrieben werden.
Soweit ich das beurteilen kann, gehört die Bytereihenfolge meistens zu "Big Endian" oder "Little Endian". Sie müssen also herausfinden, welche.
In diesem Fall habe ich herausgefunden, dass es ein wenig tolandisch ist, da die folgende Beschreibung in der Spalte "** Grundstruktur **" der Referenzseite enthalten ist.
The file is a binary file with little endian format. Float values are stored as IEEE 754 floating-point "single format".
Schließlich werden wir die Daten nach dem Header lesen. Auf der Referenzseite werden der Datentyp und die Länge jedes Blocks wie in der folgenden Tabelle gezeigt definiert (Teilauszug).
Sehen Sie sich zunächst die Datenbeschreibungsspalte ganz rechts an und wählen Sie die Daten aus, die Sie extrahieren möchten. Überprüfen Sie nach der Entscheidung, welche Daten extrahiert werden sollen, den Datentyp (Variablentyp) und den Versatzwert.
Der Versatz ist die Information der Position relativ zum Referenzpunkt und repräsentiert die Adresse der Daten. Da diesmal ein Datensatz 144 Bytes enthält, bedeutet dies, dass die Anzahl der Bytes angezeigt wird, in die die Daten geschrieben werden.
This should close the 144 byte frame.
Ich habe oben viel über Binärdaten organisiert. Wir verwenden ein Modul namens ** struct **, um diese Binärdaten zu verarbeiten. Offizielles Dokument
Das offizielle Dokument enthält die folgende Tabelle, in der die Zeichen festgelegt sind, die die Bytereihenfolge darstellen.
Wie im Kapitel [Byte Block](#Byte Block) bestätigt, haben alle Daten ihren eigenen Datentyp (valiable type).
Es ist notwendig, die Verarbeitungsmethode abhängig vom Datentyp zu ändern, jedoch in struct
, solange Sie die Datentypinformationen übergeben
Danach fühlt es sich so an, als würde es alles tun, um der Form zu entsprechen.
Das Format kann jedoch vom offiziellen Dokument abweichen, daher müssen Sie es entsprechend lesen. In diesem Fall ist es wie folgt.
short int → unsigned short(H)
int → unsigned long(L)
byte(int) → unsigned char(B)
Sie kann gelesen werden, indem Sie die Option beim Öffnen der Datei auf "rb (read binary)" setzen.
with open(file_name, 'br') as f:
data = f.read()
Sie können die gelesenen Binärdaten mit der Funktion struct.unpack_from ()
konvertieren.
Das Grundformat ist "struct.unpack_from (Datentyp, Daten, Offset)".
Ich kenne den Datentyp und den Offset bereits, also muss ich ihn nur angeben!
Unten ist das große Bild. Es sieht länger aus als ich erwartet hatte, aber im Grunde passe ich zuerst den Header an und wiederhole dann die Arbeit des Auspackens, indem ich den Versatz um die Anzahl der Elemente verschiebe.
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
#Shift um 10 Bytes Header
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
# ---------------------------------------------------------------------------------------------------
#Definieren Sie Offset und Datentyp für jedes Element
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]
#In einer Datenzeile kombinieren
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