Ich ziehe I2S Daten (16bit PCM, Big Endian, 2 Kanäle) mit 4 MBit/s über USB via Raspi Zero vom FPGA auf eine SD-Karte, dabei verliert der Empfänger gelegentlich die ersten Bytes. Resultiert daraus ein Frame-Fehler, ist auf beiden Kanälen effektiv nur weißes Rauschen. Der Fehler lässt sich manuell einfach beheben - ein oder drei Bytes entfernen bis der Rahmen wieder stimmt. Schön wäre es, wenn das sich mit irgendeiner Heuristik automatisieren ließe. Natürlich könnte ich eine Präambel mitsenden, aber die müsste sendeseitig eingefügt werden. Gibt es noch andere Möglichkeiten, weisses Rauschen in einer begrenzten Anzahl führender Frames zu detektieren?
Burkhard K. schrieb: > Ich ziehe I2S Daten (16bit PCM, Big Endian, 2 Kanäle) mit 4 MBit/s über > USB via Raspi Zero vom FPGA auf eine SD-Karte, dabei verliert der > Empfänger gelegentlich die ersten Bytes. Wie werden die Daten denn zwischen FPGA und Raspberry übertragen? Ich würde da eher mal nach der Ursache gucken und die beheben. Burkhard K. schrieb: > Schön wäre es, wenn das sich mit > irgendeiner Heuristik automatisieren ließe. Klar, wenn der neu empfangene Abtastwert nicht zu den schon vorher empfangenen passt. Wobei das halt auch schwierig wird wenn die Abtastrate im Vergleich zur Frequenz gering ist. Mit 16 Bits hast du leider keine freien Bits in zwei Bytes. Du könntest aber für jeden Abtastwert 3 Bytes übertragen und die Bits dann so verteilen: 00000DD0 DDDDDDD1 DDDDDDD1 Dann ist zumindest mit den 3 LSBs der Rahmen klar. Zuerst muss eines mit 0 hinten kommen und dann zwei mit 1 hinten. Ich verwende UART als Übertragung zwischen FPGA und PC. Verlorene Bytes hatte ich noch nie.
Gustl B. schrieb: > Ich würde da eher mal nach der Ursache gucken und die beheben. Die ist bekannt: RaspianOS auf dem Zero ist kein RealTime-OS und das Dateisystem liegt genau auf der SD-Karte, auf der die Daten landen. Schau mir deshalb gerade Tiny Core (http://tinycorelinux.net/11.x/armv6/releases/RPi/) an. Bei Übertragung zum Laptop tritt das Problem nicht auf, aber den kann/will ich nicht "ins Feld" mitnehmen. Gustl B. schrieb: > für jeden Abtastwert 3 Bytes übertragen 12MBaud sind bereits das Limit meines UARTS (nicht 4MBaud wie fälschlicherweise oben geschrieben). Deshalb (und aus weiteren Gründen) will ich das Einfügen von zusätzlichen Bytes lieber vermeiden. Gustl B. schrieb: > Klar, wenn der neu empfangene Abtastwert nicht zu den schon vorher > empfangenen passt. Das war meine Frage - wie läßt sich weißes Rauschen, d.h. fehlende Korrelation der Abtastwerte mit einem (z.B. numpy-)Skript möglichst einfach diagnostizieren? Visuell ist die Situation ja eindeutig. Inzwischen gefunden: https://machinelearningmastery.com/white-noise-time-series-python/ Der Mittelwert tendiert gegen 0 und die Standardabweichung gegen 1. Werde heute mal schauen, ob ich mit meinen Daten passende Schwellwerte finde und wieviele Pakete ich anschauen muss, um eine sichere Entscheidung zu treffen.
Burkhard K. schrieb: > RaspianOS auf dem Zero ist kein RealTime-OS und das > Dateisystem liegt genau auf der SD-Karte, auf der die Daten landen. Das trifft aber auch auf den Laptop zu. Welchen UART Baustein verwendest du? Vielleicht kannst du größere Puffer einstellen oder den häufiger abfragen. Burkhard K. schrieb: > Das war meine Frage - wie läßt sich weißes Rauschen, d.h. fehlende > Korrelation der Abtastwerte mit einem (z.B. numpy-)Skript möglichst > einfach diagnostizieren? Tja ... finde ich nicht sehr zielführend. Du wirst da immer einige viele Abtastwerte verlieren.
I2S hat mit Word-select eine Synchronisierung, USB ist auch eher packetorientiert. Ich denke dein Problem ist das Protokoll was du über USB schiebst. Vielleicht solltest du da einen Frame-Sync einbauen. BTW. I2S müsste auch auf der Stiftleiste des RPI liegen.
Er überträgt die Daten per UART zum Raspberry. Also weder USB noch I2S.
Ich danke Euch für Eure Bemühungen, mich auf den rechten bzw zielführenden Weg zu bringen. Wie gesagt arbeite ich bereits daran, das Problem der "dropped Bytes" zu eliminieren. Das ist aber nicht Thema dieses Threads. (Das Kind liegt bereits im Brunnen und ich habe eine Sammlung an "kaputten" Daten, die ich trotzdem auswerten will.) Inzwischen habe ich ein bisschen mit numpys std(), median() und histogram() herumgespielt. Damit lässt sich die Situation tatsächlich recht gut erfassen, ich muss jetzt nur noch geeignete Schwellwerte bestimmen. @Gustl - tatsächlich ist die von mir ursprünglich gewählte Kombination aus Zero, RaspianOS und SD-Karte der Grund für die "dropped Bytes": * Single CPU * 512 MByte RAM * Filesystem auf SD-Karte Der empfangende Prozess konkurriert mit dem Betriebssystem um die einzige CPU, selbst wenn er mit RT-Priorität läuft. Hinzu kommen Latenzen durch das Logging auf die SD-Karte bzw. Lesen von derselben. Tiny Core sollte helfen, da es nach dem Booten nur noch vom RAM-FS läuft. Wenn nicht, muss ich wohl auf ein Gerät mit mehr als einer CPU (und höherem Stromverbrauch) ausweichen. Oder auf Ethernet. yesitsme schrieb: > ist das Protokoll was du über USB schiebst Das Protokoll (Seriell über USB) ist nicht das Problem, siehe oben.
Burkhard K. schrieb: > Der empfangende Prozess konkurriert mit dem Betriebssystem um die > einzige CPU, selbst wenn er mit RT-Priorität läuft. Ja, genau. Verwendest du Python um vom UART aufzunehmen? Wie machst du das? Das kann man blocking und nonblocking machen. Welche Datenmenge musst du denn in welcher Zeit wegschaufeln? Der UART kann 12 MBaud, aber wie viele Bytes/s müssen tatsächlich übertragen werden? Wie machst du das im FPGA, schreibt der stumpf Bytes raus sobald der UART wieder Ready meldet oder sendet der nur auf Anfrage vom PC? Ich habe für mich zwei Varianten gebaut: 1. Stumpfes Senden. Der FPGA sendet ohne Anfrage. Da lese ich dann am PC immer. Allerdings weiß ich beim Lesen natürlich nicht wo im Paket ich neue Daten bekomme. Da muss man dann also eine Art Schieberegister implementieren. Ganz grob (mit 3 Bytes und LSB Framing):
1 | ser = serial.Serial("\dev\gerät", 921600, timeout=0) |
2 | werte = [] |
3 | sr = [] |
4 | und dann regelmäßig: |
5 | data = ser.read(100000) #lese alles was im puffer steht |
6 | if len(data) > 0: |
7 | sr.extend(data) |
8 | if len(sr) >= 3: |
9 | if (sr[0] & 1 == 0) and (sr[1] & 1 == 1) and (sr[2] & 1 == 1): |
10 | wert = (sr[0] >> 1)*128*128 + (sr[1] >> 1)*128 + (sr[2] >> 1) |
11 | werte.append(wert) |
12 | else: |
13 | sr = sr[1:len(sr)] |
14 | print ("RESYNC") |
2. Senden auf Anfrage. Im FPGA werden die Werte in einen FIFO geschreben. Der PC fragt dann eine Anzahl an Werten an und leist so lange bis er die auch bekommt. Weil da bekannt ist wie viele Werte kommen und wo der Anfang ist braucht es kein Framing.
1 | ser = serial.Serial("\dev\gerät", 921600, timeout=0) |
2 | werte = [] |
3 | und dann regelmäßig: |
4 | N = 10 # also 2**10 zu lesende Werte je Lesetransaktion |
5 | uart_data = bytearray() |
6 | ser.write(bytearray([10])) |
7 | ser.flushOutput() |
8 | ser.flushInput() |
9 | while ((len(uart_data) < 2**(N+1): # also 2**11 Bytes bei 2 Byte je Wert |
10 | data = ser.read(2**(N+1)) |
11 | uart_data.extend(data) |
12 | for x in range(0,N): |
13 | werte.append((uart_data[x*2])*256+(uart_data[x*2+1])) |
Das Problem bei begrenzter Rechenleistung ist, dass wenn du jetzt Fehler zu erkennen versuchst, dann braucht das auch Rechenleistung. Du brauchst dann also noch mehr Rechenleistung oder es gibt mehr Fehler.
:
Bearbeitet durch User
Statt if len(sr) >= 3: muss da natürlich while len(sr) >= 3: stehen.
Manchmal hilft etwas Abstand zum Problem: 1. Die einfachste Heuristik, um Framing-Fehler zu erkennen, schaut auf die Dateigröße in Bytes:
1 | frame_offset = (len(fn) % 4); |
2 | if (frame_offset) { |
3 | remove_initial_bytes_from_file(fn, frame_offset); |
4 | }
|
Erkennt natürlich nicht alle Fehler, aber den größten Teil - und den verbleibenden Rest kann ich manuell bearbeiten. Korrelation und weisses Rauschen können in der Schublade bleiben. Merke: "Too much DSP can make you short sighted" ;) 2. Das Schreiben einer mehrere MByte großen Datei auf SD kann ein paar Sekunden dauern - in dieser Zeit entnimmt der empfangende Prozess keine Bytes aus dem Puffer. Abhängig von der gewählten Puffergröße kann es zum Overrun kommen. Um den zu vermeiden, muss entweder der Puffer vergrößert werden (1 sec entspricht grob 1 MByte) oder aber das Schreiben vom Empfang der Daten abgekoppelt werden (eigener Prozess, Datei zunächst im RAM erstellen). Werd mal schauen, wie ich das am besten in Python abfackele (z.B. SpooledTempFile) @Gustl - mein Zero ist nicht nur "kopf-" sondern auch "handlos", d.h. keine Eingabegeräte. Der Datentransfer wird vom FPGA angestoßen. Die Daten kommen dort aus dem RAM. CTS ist FPGAseitig (Nexys4 DDR) ein Ausgang, somit eine Flusssteuerung per Hardware damit nicht realisierbar. (Habe es auch mit RTS probiert - das funktionierte aus irgendwelchen Gründen nicht). Auf jeden Fall danke für Eure Beiträge.
:
Bearbeitet durch User
Auch ohne CTS oder Eingabegeräte geht das mit der Anfrage. Der Raspberry schickt für jede Anfrage ein Byte zum FPGA. In dem Byte steht drinnen wie viele Werte das FPGA senden soll. Und dann sendet das FPGA die geforderte Anzahl. Dafür braucht man nur RX und TX. Ja, Schreiben und Lesen kannst du in zwei Threads unterteilen. Oder du schreibst jeweils nur wenige Byte auf Karte. Also z. B. 1kByte von UART empfangen und sofort auf Karte schreiben. Dann die nächsten 1kByte anfordern, der FPGA schickt die und dann schreibt man die weg.
Beitrag #6472849 wurde von einem Moderator gelöscht.
Burkhard K. schrieb: > Das Schreiben einer mehrere MByte großen Datei auf SD kann ein paar > Sekunden dauern - in dieser Zeit entnimmt der empfangende Prozess keine > Bytes aus dem Puffer. Abhängig von der gewählten Puffergröße kann es zum > Overrun kommen. Mit obiger Vermutung lag ich offenbar daneben - das eigentliche Problem ist wohl die beschränkte Puffergröße des FT2232H internen FiFos: ~4000 bit. Jetzt lese ich häufig kleine Batches (<= 512 Byte) - seit dem klappt es mit dem Datentransfer - auch ohne weiteren Protokolloverhead. Den Hinweis auf die FiFo-Größe habe ich hier gefunden: https://stackoverflow.com/questions/57592288/pylibftdi-device-read-skips-some-bytes
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.