Zahlenwerte im Format '\x80\x0f\xdd über RS232 einlesen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
juschu110
User
Beiträge: 3
Registriert: Mittwoch 18. November 2015, 22:45

Hallo an Alle,

ich hoffe sehr auf Hilfe von jemandem der sich mit Python2.7 auskennt.
Um ein kleines Spektrometer aus zu lesen habe ich mir eine GUI und den entsprechenden Code zum Abfragen der RS232 erstellt (das Spektrometer ist schon älter und hat nur eine RS232).
Das funktioniert soweit auch sehr gut, ist allerdings mit einer Ausleserate von 1Hz recht langsam, da es die Daten komplett aus der CCD-Zeile ausliest und überträgt.
Das Gerät bietet auch einen komprimierten Datenmodus, bei dem nach folgendem Algorithmus Bytes übertragen werden:

Every scan transmitted by the BTC110S will then be compressed. The compression
algorithm is as follows:

1. The first pixel data (a 16-bit unsigned integer) is compared to
• If the data different >=128,then send 3 Byte as follow
0x80
Data High
Data Low
• If the data different <128,Then send 1 Byte as follow
Data Different

2. The next pixel data is compared to previous pixel data.
• If the data different >=128,Then send 3 Byte as follow
0x80
Data High
Data Low
• If the data different <128,Then send 1 Byte as follow
Data Different

3. Repeat step 2 until all pixels have been read.

Damit überträgt das Gerät dann deutlich weniger Bytes und es klappt in 350 ms einmal alle Pixelwerte zu übertragen.
Ds Format ist wie folgt:

Pixel value----Value difference-----Transmitted bytes
185-------------0----------------------0x80 0x00 0xb9
2151-----------1966------------------0x80 0x08 0x67
836----------- -1315----------------- 0x80 0x03 0x44
118----------- -92-------------------- 0xa4
90------------ -28-------------------- 0xe4
......
(Die Striche sind nur als Platzhalter für etwas tabellenartiges.)


Das würde mir auch reichen, wenn ich denn die Daten auslesen könnte.
Mein Code, den ich zum Testen von Hand starte sieht wie folgt aus:

Code: Alles auswählen

# ...
    def oneValue(self):
        file = open('G:\\datentest.txt', 'w')
                  
        if ser.isOpen():
            global daten
            try:
                n = 0
                daten = []
                ser.flushInput()
                time.sleep(0.05)
                ser.write("S\n")
                time.sleep(0.05)
                S       = ser.readline()
                ACK     = ser.readline()
                
                # read 3 Byte and remove start flag \x80 from string
                pix0 = int((ser.read(3)).replace("\x80", "").encode('hex'), 16)  #### funktioniert gut ##########
                print 'pix0 = ', pix0 
                daten.append(pix0)
                
                while n <= 2046:
                    #if ser.inWaiting(): 
                    pixstr = repr(ser.read(1))
                    print 'pixstr =', pixstr
                                                          
                    if pixstr == repr("\x80"):
                        print 'hallo'
                        pix1 = int((ser.read(2)).encode('hex'), 16)  ##### test#######
                        print 'pix1 = ', pix1
                    else:
                        pix1 = int(ser.read(1).encode('hex'), 16)     ##### test #######
                        print 'pix1 = ', pix1
                    n +=1
            except:
                pass
            finally:
                file.close()
Das Auslesen vom ersten Pixel und umwandeln in einen korrekten Integerwert klappt gut und die Krücke bei den
anderen Werten zeigt mir zumindest, daß Werte kommen und wie sie aussehen.
Ich bin aber nicht in der Lage '0x80' zu erkennen, obwohl ich es beim ersten Pixel problemlos aus den 3 Bytes entfernen kann.

Seit 2 Wochen durchsuche ich die greifbaren Doku's aber entweder verstehe ich es falsch oder ich habe einen Denkfehler.
Kann mir jemand zeigen wie man die Daten korrekt auslesen kann und wie man mit den negativen Daten in Python umgeht?

Im Prinzip weiß ich es bei den negativen Werten:

Wenn 8 bit-Wert > 127 dann ziehe von Wert 256 ab. Aber soweit komme ich ganicht erst.
Die mit ##### test ####### markierten Zeilen sind nur die letzten kläglichen Versuche aus reiner Verzweifelung, um wenigstens mal eine Zahl zu sehen. Aber .... .

Wenn auch der Teil in der while-Schleife noch nicht korrekt ist, den bekomme ich problemlos hin, wenn ich zumindest die Werte irgendwie
korrekt gewandelt bekomme.

Könnte mir jemand helfen?

Vielen Dank

Jens
Zuletzt geändert von Anonymous am Mittwoch 18. November 2015, 23:53, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@juschu110: Die Beispieldaten verstehe ch nicht so ganz. Was soll „Value difference“ sein? Die Differenz von einem „Pixel value“ zu dem eine Zeile davor? Dann stimmen die Werte nicht, denn von 118 zu 836 ist es weiter als -92.

Der Code sieht schlimm aus!

Die Methode ist keine, das sollte also eine normale Funktion sein. Oder vielleicht auch nicht falls irgendwelche von den ganzen Variablen auf die da einfach so lustig aus der Umgebung zugegriffen wird, vielleicht doch zum Zustand des Objekts gehört auf dem `oneValue()` definiert ist. Der Name sagt nicht was die Methode *tut* und ist zu dem auch noch irreführend weil dort mehr als ein Wert verarbeitet wird.

Warum dort eine Datei geöffnet und geschlossen wird, ist mir ein Rätsel.

``global`` hat in einem sauberen Programm nichts zu suchen. Eine Funktion die etwas liest kann das als Rückgabewert an den Aufrufer zurückgeben.

Die Namensschreibweise hält sich nicht an den Style Guide for Python Code.

`ser` kommt einfach so aus dem Nichts.

Wenn die serielle Schnittstelle nicht offen ist, dann ist die Funktion eine Nulloperation. Das erscheint mir eine merkwürdige API.

`n` wird weit vor der Schleife die das dann letztendlich benutzt definiert. Da muss man dann erst wieder weiter oben im Quelltext suchen wie das eigentlich initialisiert wird. Aber die ``while``-Schleife ist sowieso die falsche Wahl wenn man weiss wie oft die Schleife durchlaufen werden muss.

Diese `encode()` und `int()` mit Basis 16 Geschichten sind nicht schön. Den Bytewert eines `str()`-Zeichens kann man mit `ord()` ermitteln, da muss man nicht über den Umweg einer Kodierung als Zeichenkette mit Hexadezimaldarstellung der Bytewert gehen die dann wieder in eine Zahl geparst wird. Bei 2-Bytewerten ermittelt man entweder beide Bytewerte und rechnet den 16-Bit-Wert aus, oder man verwendet das `struct`-Modul. Beim Arbeiten mit der `repr()`-Darstellung von Bytezeichenketten zum Vergleichen wird es dann komplett gruselig.

Wenn das Format wirklich wie im Beispiel ist, dann braucht man das erste Pixel nicht besonders behandeln. Du müsstest nur herausfinden ob als erstes Pixel jemals ein Differenzwert gelesen werden kann, und falls ja relativ zu welchem Startwert der ist. Falls als erster Wert nie ein Differenzwert kommen kann. Falls als erster Wert nie ein Differenzwert vorkommen kann, dann kann man trotzdem so tun als könnte das passieren und einfach alle Werte gleich behandeln.

Ein nacktes ``except:`` ist an sich schon eine sehr schlechte Idee, aber dort dann *jede* nur erdenkliche Ausnahme mit einem ``pass`` zu ”behandeln” ist das ungeschickteste was man in Punkto Ausnahmebehandlung machen kann. Was soll das denn bewirken? Ist Dir klar was Du da machst?
BlackJack

Nur das lesen/dekomprimieren der Pixelwerte als Generatorfunktion (natürlich ungetestet):

Code: Alles auswählen

import struct


def iter_pixels(stream):
    pixel_value = None
    for _ in xrange(2046):
        byte = stream.read(1)
        if byte == '\x80':
            pixel_value = struct.unpack('>H', stream.read(2))[0]
        else:
            assert pixel_value is not None, 'first pixel was a difference value'
            pixel_value += struct.unpack('b', byte)[0]
        yield pixel_value
juschu110
User
Beiträge: 3
Registriert: Mittwoch 18. November 2015, 22:45

Hallo BlackJack,

zuerst recht herzlichen Dank für Deine Mühe.
Daß mein 'Code' nicht den üblichen Konventionen entspricht glaube ich gern. Ich kenne sie schlicht weg noch nicht.
Aber ich lerne gern dazu.
Python war in erster Linie interessant, weil ich numpy, scipy und PIL habe. Die eingelesen Werte vom Spektrometer sollen
ja verarbeitet werden und da ist Python (so glaube ich) kaum zu schlagen.

Um auf Deine Frage nach den Differenzen einzugehen, muß ich ein bißchen ausholen.
Das Spektrometer erfasst die Intensitäten je nach eingestellter Integrationszeit und damit die Übertragung nicht zu lange benötigt,
hat man den Kompressionsalgorithmus benutzt.
Den ersten Intensitätswert überträgt es immer als unsigned 16 bit Integer-Wert. Deshalb hat mein Konstrukt auch (irgendwie) funktioniert.
Dann liest es den zweiten Wert der CCD-Zeile und der Controller im Gerät benutzt jetzt den Algorithmus.
Ist die Differnz der beiden Zahlen >= abs(128) überträgt es 3 Byte. Das erste Byte (\x80) ist ein Signal, das danach ein Highbyte und ein Lowbyte gesendet werden.
Ist die absolute Differenz kleiner als abs(128) kommt anschließend nur ein Byte als 8-bit signed Integer ohne vorheriges '\x80'. Somit wurden schon zwei Byte bei der Übertragung der 2048 Spannungswerte eingespart.
Das wird mit allen Werten gemacht bis die Spannung am letzten Pixel übertragen wurde. So dauert die Übertragung nur 350 ms für alle Pixelwerte.

Meine seltsamen Konstrukte entstanden einfach aus meiner Unfähigkeit die Bytes in Integerwerte zu wandeln. Mit repr() konnte ich sie wenigstens
auf dem Bildschirm sehen und habe sie, da sie natürlich ein bißchen schnell für mich kamen, in eine Datei geschrieben. Dort sah ich die Bytes und konnte zumindest die Sinnhaftigkeit überprüfen.

Der erste Wert allerdings war immer korrekt und den konte ich mit meiner selsamen Wandlung auch tatsächlich erfassen. Nur eben die anderen Werte nicht oder nur wenn es keine 8-bit signed Integer waren. Ich habe immer nach der Übertragung der ersten drei Bytes nach dem Signalflag '\x80' gesucht aber selbst wenn repr() es zeigte konnte keiner meiner Versuche es aus dem Byte auslesen und ich habe seeeehr viele Varianten versucht. Wie gesagt, noch keine Ahnung.

Die Fehlerbehandlung existiert mit Absicht nicht. Sonst sehe ich manchmal meine, zugegeben, dummen Fehler nicht immer. Ich will aus denen ja etwas lernen.

Am Ende meiner Versuche sollen die Werte an Matplotlib übergeben werden (klappt übrigens mit Dummywerten sehr gut) und sind als Diagramm gut zu interpretieren. Mit ascii-Werten klappt es über die RS232 ja schon sehr gut, eben nur ein bichen zu langsam.

Jetzt werde ich Deine Funktion mal ausprobieren und melde mich (hoffentlich mit Erfolg).

Vielen Dank für Deine Hilfe!

Jens
Benutzeravatar
darktrym
User
Beiträge: 784
Registriert: Freitag 24. April 2009, 09:26

Ich glaube du missverstehst da etwas. Die Aufgabe einer Fehlerbehandlung ist nicht, die eigene Unfähigeit zu verdecken sondern die Robustheit des Programms zu verbessern.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
juschu110
User
Beiträge: 3
Registriert: Mittwoch 18. November 2015, 22:45

Hallo BlackJack,

vielen Dank für die Hilfe. Ich habe tatsächlich meine Werte und sehe hier vor mir das Spektrum von meiner Schreibtischlampe.
Ich werde mir den Code von Dir jetzt ganz genau ansehen, da ich noch nicht genau weiß was die Zeilen zum Wandeln der Werte
im Einzelnen machen. Aber dafür habe ich jetzt einen Ansatzpunkt und kann mir die Doku von struct ansehen.

Ich hatte zwar viele Hilfen im Internet gefunden aber wenn man nicht weiß wonach man ganz genau suchen muß ist es manchmal schwierig etwas
zu finden.

Nochmal vielen Dank!

Jens
Antworten