C++ UDP und Variabel

Pascal24

Cadet 1st Year
Registriert
Okt. 2011
Beiträge
15
Hallo,

ich schreibe ein Programm das UDP Packete von einem GPS Gerät empfängt. Das funktioniert auch gut wenn ich die GPS-Zeit auslese aber wenn ich den Längen- und Breitengrad auslesen möchte bekomme ich immer eine Fehlermeldung.
Die Längengradposition besteht aus 8 Byte und das UDP packet wird im einer char variablen buffer[72] abgelegt.
Die Variable latitude ist als double initialisiert und bekommt ihren Wert wie folgt zu gewiesen: (Hier liegt glaub ich irgendwo der Fehler)

latitude = (buffer[30] <<56) | (buffer[29] <<48) | (buffer[28] <<40) | (buffer[27] <<32) | (buffer[26] <<24) | (buffer[25] <<16) | (buffer[24] <<8) | buffer[23];

Die Fehlermeldung besagt das, das "Shiften" von buffer[30], buffer[29], buffer[28] und buffer[32] nicht möglich ist. Mir ist aber nicht klar warum.

double latitude;
unsigned char buffer[80]

int len = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &addrlen); //UDP packet wird empfangen


latitude = (buffer[30] <<56) | (buffer[29] <<48) | (buffer[28] <<40) | (buffer[27] <<32) | (buffer[26] <<24) | (buffer[25] <<16) | (buffer[24] <<8) | buffer[23];
 
Kompilierst du mit allen angeschalteten Warnungen (bei gcc also mit -Wall)? Ich vermute dann sollte dein Compiler dich auf den Fehler hinweisen. Du versuchst, einen char um z.b. 56 bit zu shiften, was schlicht zuviel ist. Ein char selber hat 8 bit, und wenn er auf int extended wird (was gcc 4.6 bei mir z.b. tut), dann sind es immer noch nur 32 bit, d.h. die maximale Breite um die geshiftet werden könnte wäre 31 (im Falle von << von ganz hinten nach ganz vorne).

P.S.: Es heißt im Deutschen "Paket" ohne c ;)
 
Hallo,

buffer[] ist ja als Array von unsigned char deklariert. unsigned char sind eigentlich nur 8bit, kann aber je nach Plattform intern mit bis zu 64bit repräsentiert sein (bei Dir scheinbar 32bit). Deswegen funktioniert das left shift um 32 Positionen noch, mehr aber nicht mehr.
Ich glaube, Du hast hier zwei Codeschnipsel unglücklich vermischt, was Du da postest, macht nämlich eher keinen Sinn.

Ich muss aber gestehen, dass ich nicht viel weiter helfen kann, weil ich weder vom GPS-Protokoll noch von C++ eine ernsthafte Ahnung habe.

Es geht irgendwie darum, die einzelnen Bytes herauszukitzeln. Aber ein Element von Buffer ist ja eigentlich schon ein Byte, dann macht das Schieben wieder keinen Sinn. Das Schieben würdest Du machen, wenn die Information z.B. in einem 4-Byte Long Integer gespeichert ist und die einzelnen Bytes aber eine eigene Bedeutung haben, die Du herausholen willst.

Liebe Grüße

Werner
 
Ich teste das grade und es scheint so, als wäre 32 bits schieben identisch mit 0 bits schieben. Es geht also dann wieder von vorne los.
 
Du bist dir bewusst was shl macht? Ich glaube nämlich irgendwie nicht...
Hast du irgendwo das protokoll, weil es scheint mir so, alsdass du da gerade irgendwie mist baust. Das macht keinen sinn.

shl 32 bei x86 löscht ein dword (da bestehend aus 32 bit)
shl 64 bei x64 löscht ein qword.
Mehr schieben als 32 bzw 64 bit bringt nichts, da rechts mit nullen aufgefüllt wird.
 
Zuletzt bearbeitet:
Wie wärs denn damit?

http://stackoverflow.com/questions/8703047/how-to-convert-a-byte-array-into-double-in-c

schaut jetzt schön einfach aus.


Hier mal ein Beispiel: Vor und Zurückkonvertierung.

Code:
	double latitude1 = -12.5;
	double latitude2 = 0;

	unsigned char buffer[80];

	for (int i=0; i < 80; i++)
	{
		buffer[i] = 0;
	}

	for (int i=0; i < 8; i++)
	{
		buffer[i] = ((unsigned char*)(&latitude1))[i];
	}


	memcpy(&latitude2, &buffer, sizeof(double));
 
Da sizeof(double) = 8 ist, gehe ich stark davon aus, dass du einfach nur sowas machen musst.

latitude = *(double*)(buffer+23);

kopiert 8 bytes von buffer+23 in latitude. Manomann wenn ich mit hellsfools code anschaue frag ich mich schon ob gute programmierer so rar sind.
 
Zuletzt bearbeitet:
Tigerass 2.0 schrieb:
Da sizeof(double) = 8 ist, gehe ich stark davon aus, dass du einfach nur sowas machen musst.

latitude = *(double*)(buffer+23);

Hängt von der jeweiligen Architektur ab. Auf ARM führt ein Zugriff auf ein nicht aligntes Datum z.B. einfach mal zu einer fetten CPU-Exception -> bumm, aus die Maus. Etwas portabler wäre da schon die Variante, die Hellsfoul vorschlägt ... also erst mal via memcpy() die Bytes in eine double-Variable reinschaufeln.
 
Bin nicht vertraut mit ARM. Aber es sieht so aus, als ob er winsock verwendet auf einer x86 umgebung.
wenn soetwas bei arm nicht geht (verstehe deine erklärung nicht ganz), wieso dann nicht gleich
memcpy(&latitude, buf+23, sizeof(double)); aber ist ja jetzt egal.
 
Tigerass 2.0 schrieb:
Bin nicht vertraut mit ARM. Aber es sieht so aus, als ob er winsock verwendet auf einer x86 umgebung.

Möglich. Aber woraus schließt du das?

Tigerass 2.0 schrieb:
wenn soetwas bei arm nicht geht (verstehe deine erklärung nicht ganz), wieso dann nicht gleich
memcpy(&latitude, buf+23, sizeof(double)); aber ist ja jetzt egal.

Ich glaube, er wollte beide Konvertierungsrichtungen aufzeigen. Warum er allerings nicht für beide Richtungen memcpy() verwendet, weiß ich auch nicht.

Übrigens, mir fällt gerade noch ein Fallstrick ein. Diese Bytes-in-Double-Konvertieren und umgekeht funktioniert so nur dann, wenn sich beide Seiten über die Endianness der Daten einig sind. Network-Byte-Order ist ja normalerweise big endian, während Intel-Maschinen gebräuchlicherweise little endian sind. Da könnte also noch ein Umdrehen nötig sein.
 
antred schrieb:
Möglich. Aber woraus schließt du das?

Einfach so. Weils das übliche ist und keine weiteren Angaben gemacht wurden. Wie es aber scheint verwendet er keinen visual studio compiler oder gcc, die bringen standartmäßig nur eine warnung wenn man zuweit shiften will. Also wohl was exotisches :)
 
Bin mir nicht sicher ob ich SHL verstanden habe, deshalb frag ich quasi nach. Vielleicht sollte ich noch angeben das, das alles auf einem RaspberryPi laeuft und ich mit dem standard compiler vom derbian Betriebssystem compiliere.

In meinem GPS Handbuch steht, dass little endian als erstes kommt. Bedeutet also das bei Buffer23-30, Buffer 23 die kleinste Stelle der Zahl angibt, oder nicht? Deshalb hab ich gedacht ich muesste die Reihenfolge verschieben.
Wenn jetzt meine char in double umwandel dann faellt der Schritt ja einfach weg.
Kennt ihr da nen gutes Tutorial?
 
Oh also doch ARM.
Zu << / >>. Das sind operationen, die 1:1 in die assemblerbefehle shr und shl umgesetzt werden. Diese elementaren operationen verschieben alle bits deiner variable (eines registers) nach links oder rechts, um soviel wie du angegeben hast.
Bsp:
Code:
1<<2
1 in binär: 0x00000001 //8bit binärwert, bei 32 bit fasst ein prozessorregister 32 binärstellen.
1 shl 2    : 0x00000100 //ergebnis der 1<<2 operation, 0x100 == 4
4<<1
4 shl 1    : 0x00001000 //ergibt 8. Rechts wird mit nullen aufgefüllt, und binärstellen links fallen weg.
-128<<1                       //-128 = 0x10000000
-128 shl 1 :0x00000000 //ergibt 0. Aber nur bei 8 bit systemen. Unter 16 bit wäre das Ergebnis 0x100000000, sprich  256d.
//shr >> das selbe system, nur nach rechts. Man kann somit Multiplikationen und Divisionen um 2, 4, 8, 16, ... implementieren. Falls das der Compiler nicht selber schon macht.


Zu littleendian. Erstmal ein beispielcode. Sollte bei dir hoffentlich auch so laufen:
Code:
int main()
{
	char test[] = "ABCDEEEE";
	unsigned long a = 0;
	unsigned long b = 0xffffffff;
	printf("%x\n%x\n\n", a, b);
	
	memcpy(&a, test+4, 4);
	memcpy(&b, test, 4);
	printf("%x\n%x\n\n", a, b);

	a++;
	memcpy(test+4, &a, 4);
	printf("%s\n\n", test);

	a = 0xdeadbeef;
	memcpy(test+4, &a, 4);
	printf("%s\n", test);

	memcpy(&b, test+4, 4);
	printf("%x\n", b);

	return 0;
}
//ausgabe davon:
0
ffffffff

45454545
44434241

ABCDFEEE     //kleinste zahlen wie schon von dir gesagt ganz vorne gespeichert

ABCD´¥¡Ì      //komisches zeug am schluss ist die chardarstellung von der zahl 0xdeafbeef
deadbeef      //und wieder komisches zeug in zahl 0xdeadbeef umgewandelt (das musst du wahrscheinlich bei deinem gps auch einfach machen)

Tja normalerweise bekommt man von dem ganzen Zeug nichts mit, und eigentlich musst du dir darum auch keine Gedanken machen. Der Prozessor liest das richtig aus. Wikipedia erklärt es ganz gut. Les die auch mal was zum Stack und den push/pop befehlen durch, das steckt sozusagen dahinter. (Falls du das wirklich wissen willst)


Also wie gesagt, vllt. ein Link zu dem guten Gerät oder das Protokoll hier posten. Aber ich vermute stark, dass du einfach die 8 bytes bei buffer+23 in deine variable per memcpy kopieren musst.
 
Tigerass 2.0 schrieb:
Tja normalerweise bekommt man von dem ganzen Zeug nichts mit, und eigentlich musst du dir darum auch keine Gedanken machen. Der Prozessor liest das richtig aus.

Das war jetzt hoffentlich nicht auf die Endianness-Thematik bezogen, denn darüber muß er sich freilich Gedanken machen, wenn er Daten über Systemgrenzen hinweg austauschen und anschließend auch noch korrekt interpretieren möchte.
 
Ich meinte, dass er sich in seinem konkreten Fall dazu keine Gendanken machen muss. Und der pi kann sogar die Endianess umstellen hab ich mal gehört. Das wäre dann noch eine viel einfachere Lösung.
Aber ist ja alles nicht notwendig.
 
shifts um einen wert, der nicht echt kleiner ist als die breite des datentyps, sind übrigens undefined behavior.
 
Zuletzt bearbeitet:
Danke! Mit memcpy(&latitude, (buffer+23), sizeof(double)); funktioniert es bestens.
 
Zurück
Oben