Hallo zusammen,
ich habe eine ganze Menge Daten, welche ich (mit einem STM32) auf eine
SD-Karte logge. Es ist eine Mischung aus 32Bit, 16Bit und 1Bit Daten.
Aktuell logge ich alle Daten als lesbare Zeichen. Da die Logfiles
dadurch recht groß werden, wäre es sinnvoller die Daten direkt binär zu
schreiben (spart Speicherplatz und das Ausgeben/Einlesen der Dateien
wird schneller).
Mein Problem: Wie trenne ich die einzelnen Datensätze voneinander? Wenn
ich davon ausginge, dass es niemals einen Schreibfehler geben kann, dann
könnte ich natürlich von der bekannten Datensatzgröße ausgehen. Aber
wenn ich das nicht garantieren kann, dann muss ich irgendein (oder
mehrere) Trennzeichen haben, welches aber niemals in den Daten selber
vorkommen kann.
Gibt es hierfür eine sinnvolle Lösung, ausser die Daten daraufhin zu
überprüfen, dass nicht zufällig das gewählte Trennzeichen darin
vorkommt?
Danke und Gruß, Heiko
Heiko schrieb:> Da die Logfiles> dadurch recht groß werden, wäre es sinnvoller die Daten direkt binär zu> schreiben (spart Speicherplatz und das Ausgeben/Einlesen der Dateien> wird schneller).
Ist das derzeit oder auf absehbare Zeit tatsächlich ein Problem für
deine Anwendung? Nichts ist ätzender, als Log-/Konfigurationsdateien,
die nur mit Spezialwerkzeug zu lesen sind...
Heiko schrieb:> Gibt es hierfür eine sinnvolle Lösung, ausser die Daten daraufhin zu> überprüfen, dass nicht zufällig das gewählte Trennzeichen darin> vorkommt?
Jein. Sich Gedanken über das Format der Datensätze machen...
Geh mal anders ran an das Problem: Stell dir vor, es wäre keine
Logdatei, sondern eine serielle Schnittstelle. Da würdest du dir
sicherlich auch irgendein Datensatzformat ("Protokoll") ausdenken, bei
dem du jederzeit den Anfang eines Satzes erkennst. Vermutlich würdest du
noch eine Prüfsumme dranhängen.
Du musst dir halt einen Satztrenner aussuchen und diesen maskieren,
falls er in den Nutzdaten vorkommt.
Da Du leider nicht geschrieben hast, in welcher Reihenfolge die Daten
anfallen, kann ich Dir nur schreiben, wie ich das üblicher Weise mache.
Ich definiere meine Logdaten als struct (in C) und schreibe die Daten in
dieser Größe binär sizeof(struct ....) auf die Platte.
Dadurch ist, solange die struct statisch ist, gewährleistet, dass immer
die Satzgröße gleich ist.
Damit braucht man sich keine Satztrenner einfallen lassen und hat auch
sonst
weniger Aufwand.
Lesen geht dann ebenso über die feste Satzlänge.
Wichtig dabei ist, dass man für beide Programme (Schreiben und Lesen)
identische Compileroptionen setzt. Sonst geht das schief.
So ganz klar ist mir die Datenstruktur nicht, d.h. ob die verschieden
breiten Wörter zusammengehören oder unabhängig voneinander geschrieben
werden. Aber es gab in der alten Zeit sog. fixed length records. Ein
Marker am Anfang bestimmt den Typ des Datensatzes und von da an ist die
Wortbreite klar, bspw. könntest du die ASCII-Zeichen 1, 6 und 3
verwenden, gefolgt von jeweils dem 1-, 16- bzw. 32-bit-Wort.
Wenn hingegen immer die drei Datentypen zusammengehören, wird es nicht
besser als 7 Byte-Blöcken, da ich für das 1-bit-Wort auch ein Byte
verwenden würde. So geht es immer in 7er Schritten voran. Wenn
Speicherplatz keine Rolle spielt, würd' ich auf 8bit aufrunden und bspw.
immer einen Zeilenumbruch oder etwas ähnlich praktisches (NUL…) als
Trenner einsetzen.
Heiko schrieb:> Hallo zusammen,>> ich habe eine ganze Menge Daten, welche ich (mit einem STM32) auf eine> SD-Karte logge. Es ist eine Mischung aus 32Bit, 16Bit und 1Bit Daten.
Du solltest vielleicht erläutern, ob Deine Datenmischung immer im selben
Verhältnis ensteht (was die Struct-Lösung oben begünstigen würde), oder
in beliebigen Mischverhältnissen.
Heiko schrieb:> Mein Problem: Wie trenne ich die einzelnen Datensätze voneinander? Wenn> ich davon ausginge, dass es niemals einen Schreibfehler geben kann, dann> könnte ich natürlich von der bekannten Datensatzgröße ausgehen. Aber> wenn ich das nicht garantieren kann, dann muss ich irgendein (oder> mehrere) Trennzeichen haben, welches aber niemals in den Daten selber> vorkommen kann.>> Gibt es hierfür eine sinnvolle Lösung, ausser die Daten daraufhin zu> überprüfen, dass nicht zufällig das gewählte Trennzeichen darin> vorkommt?
Die typische Lösung ist sowas wie 4 Byte "Synchronisationsmarker" im
Datenstrom zu platzieren, die am Anfang eines Datensatzes stehen.
Die Wahrscheinlichkeit, dass in deinen Daten zufällig genau diese 4 Byte
vorkommen ist verschwindend gering und selbst wenn es passiert,
wiederholt die Synchronisationslogik die Suche nach dem Marker einfach
für das nächste Paket noch einmal, sodass du nur ein Paket verlierst.
Also Idee ist, jedes Paket hat einen Marker am Anfang, und dann wird
einfach kontinuierlich gelesen außer der Marker passt irgendwo nicht; in
dem Fall ignorierst du einfach alle Daten bis zum nächsten Marker. Das
ist sehr robust.
Sven B. schrieb:> Die Wahrscheinlichkeit, dass in deinen Daten zufällig genau diese 4 Byte> vorkommen ist verschwindend gering und selbst wenn es passiert,> wiederholt die Synchronisationslogik die Suche nach dem Marker einfach> für das nächste Paket noch einmal, sodass du nur ein Paket verlierst.
Als erste Lösung gut, aber unsauber. Darum wird das "zufällige
Verlieren" mit Byte-Stuffing (hier Long-Stuffing) auf 0 reduziert. Sagen
wir die Magic-Marker-Number soll 128, dann definieren wir noch eine
weitere (129) und ersetzten jedes "natürliche" Auftreten von 128 oder
129 durch zwei Zeichen: 129, (Zeichen + 10).
Der Start-Marker ist damit weiterhin 128, und wenn 129 auftaucht, wird
die 129 gelöscht und das nächste Zeichen -10 genommen.
Achim S. schrieb:> Sven B. schrieb:>> Die Wahrscheinlichkeit, dass in deinen Daten zufällig genau diese 4 Byte>> vorkommen ist verschwindend gering und selbst wenn es passiert,>> wiederholt die Synchronisationslogik die Suche nach dem Marker einfach>> für das nächste Paket noch einmal, sodass du nur ein Paket verlierst.>> Als erste Lösung gut, aber unsauber. Darum wird das "zufällige> Verlieren" mit Byte-Stuffing (hier Long-Stuffing) auf 0 reduziert. Sagen> wir die Magic-Marker-Number soll 128, dann definieren wir noch eine> weitere (129) und ersetzten jedes "natürliche" Auftreten von 128 oder> 129 durch zwei Zeichen: 129, (Zeichen + 10).>> Der Start-Marker ist damit weiterhin 128, und wenn 129 auftaucht, wird> die 129 gelöscht und das nächste Zeichen -10 genommen.
Hmja, ist nett, aber ist das wirklich nötig? Daten verlierst du eh nur
dann wenn sowieso schon was fehlerhaft ist, und dann höchstens ein Paket
mehr als beschädigt wurde ... mit einer typischerweise völlig
verschwindenden Wahrscheinlichkeit bei 4 Byte Marker ...
Heiko schrieb:> dann muss ich irgendein (oder> mehrere) Trennzeichen haben, welches aber niemals in den Daten selber> vorkommen kann.
(1) Mit Endezeichen (bspw. irgendein Byte). Kommt das Endezeichen in den
Daten vor, wird es dort doppelt geschrieben. Records werden als
Bytesequenzen betrachtet und können unterschiedliche Länge haben.
(2) Bei ASCII benutzt du nur einen Teil des Zeichensatzes für die
"Zahlen". Einen anderen Teil für Markierungen. Definierte dir deinen
eigenen Zeichensatz, der dichtere Daten erlaubt (z.B. BCD, BASE64,
etc.).
(3) Führe eine Checksum ein, die an das Ende des Records angehangen
wird. (Damit sollte man schnell synchronisieren können.)
> ich habe eine ganze Menge Daten, welche ich (mit einem STM32) auf eine> SD-Karte logge.
In einem Thread hier im Forum wurde kürzlich die (un)Eignung von SD
Karten für zuverlässiges Speichern diskutiert.