Datenübertragung über große Strecken

 Autoren: Grujic, Heemann

Für die Datenübertragung über große Strecken bieten sich eine Vielzahl von Möglichkeiten an, klassischerweise wird diese über kabelgebundene Lösungen realisiert und bietet zum Beispiel bei der Verwendung von Lichtwellenleitern hohe Übertragungsraten bei gleichzeitiger, nahezu verlustlosen Übertragung des Signals. Bei vielen mobilen Anwendungen ist jedoch eine kabelgebundene Verbindung nicht realisierbar, daher werden hier drahtlose Kommunikationsformen verwendet. Die Übertragung von Daten über große Strecken mit Hilfe von drahtlosen Verbindungen stellt jedoch eine größere technische Herausforderung dar. Zum einen nimmt die Signalstärke mit zunehmender Distanz ab, hierfür Verantwortlich ist die Freiraumdämpfung (Abnahme der Leistungsdichte mit größer werdendem Abstand), des Weiteren sind die Daten störempfindlicher und müssen für die Übertragung zunächst in ein übertragbares Signalformat moduliert und anschließend demoduliert werden.


Hardwareaufbau


Für die Kommunikation von Ballonsonde zu Bodenstation werden zwei Sendemodule verwendet, welche die zu übermittelnden Informationen mittels Frequenzmodulation übertragen. Hierbei wird der Träger, häufig eine einfach Sinus-Schwingung, durch das zu übertragende Signal in ihrer Frequenz verändert. Über die Demodulation kann aus der Frequenzänderungen des Trägers das Signal zurückgewonnen werden. Als Sendemodule wurden das Baofeng UV5-R, sowie das Radiometrix NTX2 eingesetzt. Gesteuert werden diese beiden Sendemodule mit Hilfe eines Raspberry Pi B (plus). Die zu übertragenden Daten setzen sich aus ermittelten Messwerten für Temperatur, Luftdruck und –feuchte, Sauerstoffgehalt, UV-Strahlung, Radioaktivität und Spektrometriemessung, sowie den, über eine Raspberry Pi Kamera aufgenommenen, Bilddaten während des Fluges.

 Raspberry Pi 

raspberrypi

Der Raspberry Pi ist ein kreditkartengroßer Computer, welcher von der Raspberry Pi Foundation entwickelt wurde. Die Stiftung wurde am 5. Mai 2009 durch David Braben und einiger weiterer Computerexperten gegründet und verfolgt das Ziel jungen Menschen eine kostengünstige Plattform zu liefern und die Möglichkeit zu bieten erste Erfahrungen in der Programmierung und Ansteuerung von Computerschnittstellen zu sammeln.

Ermöglicht wurde die Realisierung des Projektes erst durch den starken Zuwachs an Smartphones und die Entwicklung kleiner, günstiger und leistungsstarker Prozessoren. Die Grundlage des Raspberry Pi ist ein Broadcom BCM2835, ein System on a Chip, welches vorher hauptsächlich in Mobiltelefonen Anwendung gefunden hat. Das verwendete Betriebssystem ist, sofern es den vorhandenen Prozessor unterstützt, frei wählbar. Als Standard gilt das Raspbian Betriebssystem (aktuelle Version Wheezy, Stand 2015), eine auf den Raspberry angepasste Debian Distribution. Neben der Verwendung als Heimcomputer, genießt der Raspberry auch große Popularität als Mediencenter, oder als Plattform für die Entwicklung computergestützter Projekte und eingebetteter Systeme (embedded sytems). In diesem Projekt wird der Raspberry als Zentraleinheit eingesetzt, welche die Sensorplatine per I2C ansteuert und mittels der Raspberry Kamera Bilder aufnimmt. Die digitalen Bild- und Messdaten werden anschließend codiert und in AX25 Format umgewandelt. Anschließend werden diese Daten mit Hilfe des TNC-Pi in Audiosignale konvertiert und an die Basisstation übertragen.

Übertragung der Messwerte an den Raspberry Pi

Während des Fluges wird eine Vielzahl von Messwerten aufgenommen, die hierfür benötigten Sensoren steuert ein Microcontroller der MSP430-Serie aus dem Hause Texas Instruments. Die aus den analogen und digitalen Sensoren ermittelten Messwerte werden über diesen in numerische Werte umgewandelt und an einen Raspberry Pi übertragen, als Übertragungsprotokoll wird der I2C-Standard verwendet.

I2C-Kommunikation - gut kommuniziert

Für die I2C-Kommunikation zwischen dem Raspberry Pi und MSP430 wird die WiringPi Library verwendet.

Die WiringPi Library wird über Git heruntergeladen und auf dem Raspberry Pi kompiliert.

sudo apt-get update sudo apt-get upgrade
sudo apt-get install git-core
git clone git://git.drogon.net/wiringPi
cd wiringPi
git pull origin
cd wiringPi
./build

 

Nach der Installation kann die Library verwendet werden. Es ist darauf zu achten, dass die jeweiligen Header, in unserem Fall

#include <wiringPiI2C.h>

 eingebunden und beim kompilieren die Library gelinkt wird.

gcc foo.c -o foo -l wiringPi

 

Der RaspberryPi kann mit WiringPi nur als I2C-Master eingesetzt werden. Die I2C GPIOs verwenden integrierte 1,8 kOhm Pull-Up-Widerstände. Pinbelegung und die jeweiligen Bezeichnungen können individuell, je nach Modell, mit

gpio readall

ermittelt werden.

GPIOreadall

 

Die SDA.1 und SCL.1 sind die Daten- und Clockleitung der I2C-Schnittstelle des RaspberryPi und haben in der wiringPi Library die GPIO Nummerierung 8 und 9.

 

Die Steuerung der Sensorik wird durch einen Mikrocontroller MSP430 übernommen. Weitere Details können hier nachgelesen werden. Der RaspberryPi steuert über die I2C-Schnittstelle die Messintervalle des Mikrocontrollers und fragt die Messwerte ab. Die zu übertragenen Datenpackete werden vom MSP430 in folgender Struktur bereitgestellt:

Kontrollbyte Messwert
6 CRC-Bits Anzhl der folgenden Bytes pro Messwert (2 Bit) MSB LSB
101010 10 xxxxxxxx xxxxxxxx

 

Die Messwerte der Sensorik benötigen pro Messwert zwischen einem und drei Bytes. Die Packetübertragung startet mit einem Kontrollbyte aus 6 Bit Prüfsumme und 2 Bit, die die Länge des folgenden Messwerts in Bytes angibt. In der oben dargestellten Tabelle ist ein Datenpacketpaar für einen Messwert, bestehend aus zwei Bytes, dargestellt. Nach dem erläuterten Packetformat  ist sichergestellt, dass sowohl der RaspberryPi , als auch die Bodenstation nach der Funkübertragung der Messdaten die Vollständigkeit und korrekte Übertragung prüfen kann.

Folgende Funktion prüft das Datenpacket:

  • Prototyp:
int i2c_check_data(uint8_t* raw_data);
  • Parameter:
    • raw_data: erwartet einen uint8_t Pointer auf das Array des Datenpackets
  • Return Value: bei korrekter Übertragung 0, sonst -1
int i2c_check_data(uint8_t* raw_data)
{
    uint8_t j=0, l=0, data_len;
    while(j < RAW_DATA_LEN ) // läuft für die Anzahl an Datensätzen
    {
        if((*(raw_data+j) & 0b11111100) != 0b10101000) // Überprüft die CRC-Bits
        {
	    printf("checkbyte: %i, länge: %i \n",*(raw_data+j) & 0b11111100 , (*(raw_data+j) & 0b00000011) +1);	
		printf("data check found invalid data...STOP\n");
            return -1;                                                             
        }
        data_len = (*(raw_data+j) & 0b00000011) + 1;  // Anzahl der Bytes des jeweils folgenden Datensatzes wird ermittelt
        l +=1;                                                                       
        j += data_len+1;
    }
    return 0;
}

Folgende Funktion rekonstruiert die Messdaten:

  • Prototyp:
int i2c_convert_data(uint8_t* raw_data);
  • Parameter:
    • raw_data: erwartet einen uint8_t Pointer auf das Array des Datenpackets
  • Return Value: gibt den Pointer auf das Array mit den konvertierten Daten zurück.
uint32_t* i2c_convert_data(uint8_t* raw_data)
{
    static uint32_t final_data[FINAL_DATA_LEN];
	int8_t j= 0, k,l = 0, data_len;
	while(j < RAW_DATA_LEN ) // läuft für die Anzahl an Datensätzen
	{
        data_len = (*(raw_data+j) & 0b00000011) +1; // Anzahl der Bytes des jeweils folgenden Datensatzes wird ermittelt
        final_data[l] = 0;  // final_data-Elemente mit 0 initialisieren, um Bitshiftfehler zu vermeiden
        for(k = data_len; k >= 1; k--) // läuft für die Anzahl der Bytes pro Datensatz
		{
			final_data[l] <<=  8 ;	 // shiftet um 8 Bit nach links
			final_data[l] +=(*(raw_data+j+k) & 0xFF) ; // hängt nächstes Byte an
		}
		l +=1; // Anzahl des final_data-Arrays
		j += data_len+1;
	}
	
	printf("I2C: Final Data...\n");
	int i;
        for(i=0; i<(sizeof(final_data)/sizeof(final_data[0]));i++)
        {
		printf("%i --> %i\n",i,final_data[i]);
	}
    return final_data;
}

 

Sicherung der Messwerte

SQLite - schlank und effektiv

Die lokale Speicherung der Daten wird mit SQLite erledigt. Umfassende Dokumentationen und Tutorials zur Verwendung mit C erleichtern den Einstieg. Als eine der standard, embedded Datenbanken kommt SQLite schlank und ohne Server daher und lässt sich als einzelne Datei pro Datenbank sehr leicht kopieren und sichern. Beim Einsatz der SQL Querys sind gängige Abfragen, wie 

SELECT * FROM `foo`;

einsetzbar. Durch die Single-File Lösung sind jedoch nicht alle gewohnten MySQL-Schlüsselwörter verwendbar. Beispielsweise verfügt SQLite nicht über 'TRUNCATE TABLE', so dass das Leeren einer Tabelle über alternative Wege, wie 'DELETE' und 'DROP' zu lösen ist.

Im konkreten Fall des Stratusphärenballonprojekts werden die Messdaten der Sensorik lokal auf der SD-Karte des RPi gespeichert, um Datenverlust zu vermeiden, falls die Funkübertragung ausfallen würde und in kleineren Intervallen loggen zu können, als per Funk zu senden, um den Energieverbrauch zu reduzieren und gleichzeitig nicht auf eine hohe, zeitliche Auflösung verzichten zu müssen.

Die aktuelle C Library für SQLite3 wird wie folgend installiert:

sudo apt-get install libsqlite3-dev

Eine effektives Konsolenverwaltungsprogramm für SQLite3 kann zusätzlich mit:

sudo apt-get install sqlite3

 installiert werden.

Wer beim kompilieren noch an das Linken der Library denkt

gcc foo.c -o foo -l sqlite3

kommt mit dem folgenden, simpel gehaltenem Quellcode aus und loggt fröhlich Messwerte in die Datenbank:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sqlite3.h>
	sqlite3 *db;
	int status;
	char* zErrMsg = 0;
	char* query;
	sqlite3* db_connection;
void db_init()
{
	printf("DB: Initialisierung...\n");
	status = sqlite3_open("skylab.db", &db_connection);
	if( status ){fprintf(stderr,"Error opening db: %s \n", sqlite3_errmsg(db_connection));}
	query =	"CREATE TABLE IF NOT EXISTS `data`(id INTEGER PRIMARY KEY, id_msp INTEGER, " \
		"temp_innen1 INTEGER, temp_innen2 INTEGER, temp_aussen1 INTEGER," \
		"temp_aussen2 INTEGER, temp_p1 INTEGER, temp_p2 INTEGER, temp_feuchte INTEGER, druck_p1 INTEGER," \
		"druck_p2 INTEGER, rel_luftfeuchte INTEGER, uv1 INTEGER, uv2 INTEGER," \
		"acc_x INTEGER, acc_y INTEGER, acc_z INTEGER, mag_x INTEGER, mag_y INTEGER," \
		"mag_z INTEGER, gyr_x INTEGER, gyr_y INTEGER, gyr_z INTEGER, geiger INTEGER," \
		"sauerstoff1 INTEGER, sauerstoff2 INTEGER, spectro1 INTEGER, spectro2 INTEGER," \
		"spectro3 INTEGER,spectro4 INTEGER,spectro5 INTEGER,spectro6 INTEGER," \
		"spectro7 INTEGER,spectro8 INTEGER,spectro9 INTEGER, Timestamp DATETIME DEFAULT CURRENT_TIMESTAMP);";
	status = sqlite3_exec(db_connection, query, 0, 0, &zErrMsg);
	if ( status != SQLITE_OK )
	{
		fprintf(stderr,"SQL error: %s \n", zErrMsg); 
		sqlite3_free(zErrMsg);
	}
}
void db_insert_measured_data(uint32_t* data)
{
	query = sqlite3_mprintf("INSERT INTO `data` ( id_msp, " \
		"temp_innen1, temp_innen2, temp_aussen1," \
		"temp_aussen2, temp_p1, temp_p2, temp_feuchte , druck_p1, " \
		"druck_p2, rel_luftfeuchte, uv1, uv2, " \
		"acc_x, acc_y, acc_z, mag_x, mag_y," \
		"mag_z, gyr_x, gyr_y, gyr_z, geiger," \
		"sauerstoff1, sauerstoff2, spectro1, spectro2," \
		"spectro3,spectro4,spectro5,spectro6," \
		"spectro7,spectro8,spectro9) VALUES(%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i);"
		,*data,*(data+1),*(data+2),*(data+3),*(data+4),*(data+5),*(data+6),*(data+7),*(data+8),*(data+9),*(data+10),*(data+11),*(data+12),*(data+13),
		*(data+14),*(data+15),*(data+16),*(data+17),*(data+18),*(data+19),*(data+20),*(data+21),*(data+22),*(data+23),*(data+24),*(data+25),*(data+26),
		*(data+27),*(data+28),*(data+29),*(data+30),*(data+31),*(data+32),*(data+33));
	status = sqlite3_exec(db_connection, query, 0, 0, &zErrMsg);
	if ( status != SQLITE_OK )
	{
		fprintf(stderr,"SQL error while inserting: %s \n", zErrMsg); 
		sqlite3_free(zErrMsg);
		sqlite3_free(query);
	}		
		
}

 

Übertragung der Messwerte an die Bodenstation

Baofeng UV5-R

Baofeng SchnittstelleanalogeEinundAusgänge

Das Baofeng UV5-R ist ein kostengünstiges Dualband Amateurfunk Handgerät, welches im zwei Meter Band (136-174 MHz), sowie im 70 Zentimeter Band (400-480MHz) senden und empfangen kann und bis zu fünf Watt Ausgangsleistung liefert. Für die Übertragung der Bild- und Messdaten wurde dieses Gerät, im 70 Zentimeter Band mit einer Ausgangleistung von einem Watt, betrieben. Zur Ansteuerung und Datenübertragung an das Baofeng UV5-R wurde zunächst die Verkleidung entfernt und anschließend die serielle Schnittstelle, bestehend aus TX-Audio (Transmitter) und RX-Audio (Receiver), sowie der PTT-Pin (Push-to-Talk) und Ground (Massepotential) auf der Platine lokalisiert. Zur Steuerung des Funkgerätes und Übermittelung der digitalen Daten von der Zentraleinheit wird die aus den Tx-Audio, Rx-Audio, PTT-Pin und Ground bestehende Schnittstelle verwendet. Wird der PTT-Pin auf Masse gezogen, wechselt das Funkgerät in den Sendebetrieb und funkt die an dem TX-Audio-Eingang anliegenden Daten. Die Daten müssen jedoch als Audiosignal anliegen.

 Kommunikation Raspberry Pi und Baofeng UV-5R

 

TNC-Pi

Baofeng TNC

Als Schnittstelle zwischen der Zentraleinheit, einem Raspberry Pi B (plus), und dem Baofeng UV5-R dient das TNC-Pi, ein Terminal Node Controller, welcher auf dem Raspberry Pi verwendet werden kann. Über die serielle Schnittstelle des Raspberry Pi werden die zu sendenden Daten an das TNC-Pi übertragen und mittels Audio Frequenz Shift Keying (AFSK) in Audiosignale umgewandelt. Das TNC-Pi ist mit Tx-Audio, Rx-Audio, PTT-Pin und Ground des Baofeng UV-5R verbunden. Als Übertragungsprotokoll wird das AX25 Format verwendet.

 

Installation TNC-Pi

Das TNC-Pi wird offiziell von Coastel Chip Works vertrieben und wird als Aufsteckmodul verwendet. Für die Verwendung des Moduls muss der Raspberry Pi zunächst konfiguriert werden.

Das TNC-Pi kommuniziert wahlweise über I2C oder die serielle Schnittstelle des Raspberry Pi. Die vorinstallierte Linux-Distribution Raspbian (Version Wheezy) verwendet die serielle Schnittstelle standardmäßig als serielle Konsole, diese muss, bei Verwendung der seriellen Schnittstelle, deaktiviert werden.

Unter /boot/cmdline.txt werden die dazugehörigen Einträge entfernt

console=ttyAMA0, 115200

kqdboc=ttyAMA0, 115200

zusätzlich muss in /etc/inittab folgende Zeile entfernt werden

T0:23:respawn: /sbin/getty –L ttyAMA0 115200 vt100

und der Raspberry Pi neugestartet werden. Für das Kompilieren des AX25 Protokolls werden die folgende Werkzeuge und Bibliotheken benötigt

sudo apt-get install build-essential libax25-dev libx11-dev zlib1g-dev libncurses5-dev

Die Installation kann direkt aus dem Raspbian Paketmanager erfolgen

sudo apt-get install ax25-tools

sudo apt-get install ax25-apps

jedoch sind diese Pakete veraltet und es empfiehlt sich die Dateien selbst zu kompilieren. Eine Anleitung hierfür ist unter k4gbb.us/docs/Raspberry.html vorhanden.

Nach erfolgreicher Installation wird die Konfiguration des AX25 Protokolls angepasst. Unter /etc/ax25/axport passen wir Rufzeichen, Baudrate und Paketlänge an

# Port    Callsign          Baud        PacLen       MaxFrame        Descripition

   0         DN4GB-1      19200       256             7                       Baofeng Sender

Das TNC-Pi kann nur mit 19200 Baud betrieben werden. Wichtig ist es Rufzeichen und Port auch in den dementsprechend die Einstellungen des AX25 Demon unter /etc/ax25/ax25d.conf zu übernehmen

# /etc/ax25/ax25.conf

[DN4GB-1 via 0]

NOCALL * * * * * * L

Default * * * * * * -rmsgw /usr/local/bin/rmsgw rmsgw –P 0 %U

Mit beendeter Konfiguration kann das AX25 Protokoll gestartet werden

sudo ax25 start

Das Baofeng UV5-R kann nun über einen AX25 Socket gesteuert werden.

 

Programmablauf

 

RPIBlockschaltbild

Auf dem Raspberry Pi laufen während des Fluges zwei Programme parallel ab, im folgenden Baofeng Steuerprogramm und Radiometrix Steuerprogramm genannt. Das Radiometrix Steuerprogramm nimmt mit Hilfe der Raspberry Kamera Bilder während des Fluges auf und sendet diese mit 10mW an die Bodenstation. Parallel dazu dient das Baofeng Steuerprogramm zur sequentiellen Übertragung der Mess- und Bilddaten über das Baofeng UV5-R. Das Programm steuert über I2C den Microcontroller und ruft die ermittelten Messdaten ab. Nach Sicherung der Messwerte in einer SQL Datenbank werden diese über die AFSK des TNC-Pi an das Bafeng übermittelt und per FM an die Bodenstation gefunkt. Das Baofeng Steuerprogramm greift nicht aktiv auf die Raspberry Kamera zu, sondern arbeitet mit einer Kopie des akutellen Bildes.

Kommunikation über AX25 Socket

Die Programmierung zur Steuerung des Baofeng UV-5R erfolgt mit eines in C geschrieben Programmes. Das Programm startet den Messvorgang, sichert die Messdaten innerhalb einer SQL Datenbank und sendet Bild- und Messdaten an die Bodenstation.

Die Übertragung der Bild- und Messdaten erfolgt über einen AX25 Socket. Für die Initialisierung des Sockets werden die netax25 Bibliotheken verwendet und basiert auf dem AX25 Beacon.

 

#include <sys/types.h>
#include <sys/socket.h>
#include "netax25/ax25.h"
#include "netax25/axlib.h"
#include "netax25/axconfig.h"
#include "netax25/daemon.h"
// *** AX25 relevante Einstellungen ***
struct full_sockaddr_ax25 dest;
struct full_sockaddr_ax25 src;
char *addr, *portcall;
int ax25_Socket, n, dlen, len;
char srccall[7] = "DN4GB-1"; 				// eigenes Rufzeichen, muss mit axport & ax25d.config übereinstimmen
char destcall[5] = "DN4GB";
char port[1]	= "0";					// AX25 Portname / siehe axports und ax25d.config
// *** Initialisierung des AX25 Sockets ***
int ax25_init()
{
	printf("AX25: Initialisierung... \n");
	 // *** Überprüfung der AX25 Konfiguration / Sender / Empfänger / Port ***
	if (ax25_config_load_ports() == 0) {
		fprintf(stderr, "wsaprs: no AX.25 ports defined\n");
		return 1;
	}
	if ((portcall = ax25_config_get_addr(port)) == NULL) {
		fprintf(stderr, "wsaprs: invalid AX.25 port setting - %s\n", port);
		return 1;
	}
	addr = NULL;
	if (destcall != NULL)
		addr = strdup(destcall);
	if (addr == NULL)
	  	return 1;
	if ((dlen = ax25_aton(addr, &dest)) == -1) {
		fprintf(stderr, "wsaprs: unable to convert callsign '%s'\n", addr);
		return 1;
	}
	if (addr != NULL) free(addr); addr = NULL;
	if (srccall != NULL && strcmp(srccall, portcall) != 0) {
		if ((addr = (char *) malloc(strlen(srccall) + 1 + strlen(portcall) + 1)) == NULL)
			return 1;
		sprintf(addr, "%s %s", srccall, portcall);
	} else {
		if ((addr = strdup(portcall)) == NULL)
			return 1;
	}
	if ((len = ax25_aton(addr, &src)) == -1) {
		fprintf(stderr, "wsaprs: unable to convert callsign '%s'\n", addr);
		return 1;
	}
	if (addr != NULL) free(addr); addr = NULL;
	// *** AX25 Socket initalisieren  ***
	printf("AX25: Socket erstellen... \n");
	if ((ax25_Socket = socket(AF_AX25, SOCK_DGRAM, 0)) == -1) {	// Socket einrichten
		return 1;
	}
	if (bind(ax25_Socket, (struct sockaddr *)&src, len) == -1) {	// Socket binden
		return 1;
	}
}

 

Das Senden der Daten erfolgt ähnlich der Verwendung eines einfachen TCP/IP Sockets.

 

// *** Sende Daten über AX25 Socket ***
void ax25_send_data(uint8_t* send_data,int data_len,int wait_time)
{
	uint8_t* payload;
        payload=(uint8_t*) malloc(data_len);                    // Anpassung an geforderten Speicherbedarf
        int i;
        for(i=0;i<data_len;i++)                                 // Laden der Daten in Payload
        {
                payload[i]=*(send_data+i);
                printf("%d ",*(send_data+i));
        }
        printf("\n");
	printf("AX25: Sende Daten... \n");
	sendto(ax25_Socket, payload, data_len, 0, (struct sockaddr *)&dest, dlen); // Senden der Rohdaten als Bitmuster
	usleep(wait_time);
	printf("AX25: Daten gesendet \n");
	free(payload);
}

Das Senden der Daten erfolgt als Broadcast und benötigt keine vorherige Etablierung einer Verbindung. Der Zeiger send_data zeigt hierbei auf ein uint8_t Array und sendet data_len-viele Werte. Über die wait_time wird in Millisekunden definiert, welche Pause nach dem Sende der Daten erfolgen soll.

 

Das Schließen des Sockets erfolgt über die close-Anweisung

 

// *** AX25 Socket schließen ***
void ax25_close()
{
	close(ax25_Socket);
	printf("AX25: Socket geschlossen \n");
}

 

Programmablauf Baofeng UV5-R

Das Steuerprogramm für das Baofeng UV5-R ist für die Übermittelung von sowohl Bild- als auch Messdaten verantwortlich. Nach Übermittelung der Daten per I2C von dem MPS430 Microcontroller an den Raspberry Pi, liegen diese Daten in einem unit8_t Array vor und können mittels des AX25 Sockets gesendet werden. Die Übermittelung der Bilddaten erfordert zunächst eine Konvertierung in ein geeignetes Format, wir verwenden das SSDV Format.

Slow Scan Digital Video (SSDV)

Slow Scan Digital Video (SSDV) ist eine paketorientierte Übertragungsmethode für Standbild über Funkverbindungen, welches derzeit auf die RTTY Übertragungsform limitiert ist. Entwickelt und programmiert wurde diese Codierungsmethode von Philip Heron und steht als OpenSource Code zur Verfügung. https://github.com/fsphil/ssdv

Das SSDV Format besteht aus je 256 Bytes bzw. Zeichen (chars), welche sich aus Header (15 Bytes), Payload (205 Bytes) und Prüfbytes (36 Bytes) zusammensetzt. Die Payload beinhaltet die Tatsächlichen Bilddaten, während der Header für Erkennung und Dekodierung notwendig ist. (weiter Informationen: http://ukhas.org.uk/guides:ssdv)

Nach der Konvertierung liegen die Daten als Textformat vor und werden in Paketen von je 128 Zeichen versendet. Für die Unterscheidung der einzelnen Bildpakete werden diese um einen zusätzlichen Header ergänzt, welcher sich aus dem Erkennungsstring SSD, dem Bildzeichen A bzw B, sowie der Paketnummer im Hex-Format zusammensetzt.

Die Konvertierung des Bildes erfolgt in der Routine ssdv_encode_picture()

 

void ssdv_encode_picture()
{
	char copy_paste[24]="cp ssdv_temp ssdv_ax25\0";					// Erstelle Kopie des gesendeten Bildes
	system(copy_paste);
	// Codieren Bild nach SSDV --> basierend auf Philip Herons SSDV Open Source Projekt 
	int c;
	FILE *fin=NULL;
	FILE *fout=NULL;
	fin=fopen("ssdv_ax25","rb");
	fout=fopen("SSDV_DATA_AX25","wb");
	char type = SSDV_TYPE_NORMAL;
	char callsign[6]="DN4GB ";
	ssdv_t ssdv;
	uint8_t pkt[SSDV_PKT_SIZE], b[128];
	ssdv_enc_init(&ssdv, type, callsign, ssdv_index);			// Header vorbereiten (Rufzeichen, Nummer)
	ssdv_enc_set_buffer(&ssdv, pkt);					// Buffer vorbereiten
	while(1)
	{
		while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME)
		{
			size_t r = fread(b, 1, 128, fin);			// lese zu kodierendes Bild ein
        		ssdv_enc_feed(&ssdv, b, r);				// codiere Bilddaten nach SSDV
		}
		if(c==SSDV_EOI)
		{
			break;							// Datei vollständig umgewandelt
		}
		fwrite(pkt, 1, SSDV_PKT_SIZE, fout);				// schreibe codierete Daten in Datei
	}
	fclose(fin);
	fclose(fout);
}

 

und liegt nach Abschluss der Konvertierung innerhalb der Datei SSDV_DATA_AX25 vor, aus welcher bei jedem Sendevorgang 128 Zeichen ausgelesen, um einen Header erweitert und anschließend versandt werden. Das Bereitstellen des Sendearrays erfolgt über ssdv_get_data()

 

// *** Routine zum Einlesen und versenden von decodierten SSDV Bildern ***
uint8_t* ssdv_get_data()
{
	static uint8_t ssdv_data[SSDV_DATA_LEN];		// SSDV Datenarray pro Übertragung
	int char_zaehler;					// Zähler für Größe des Sendearrays
	int c;							// byteweises einlesen der codierten Bilddaten
	if(ssdv_packet==0)					// Falls neue Übertragung dekodiere Bild und öffne Datei
	{
		ssdv_encode_picture();
		ssdv_datei=fopen ("SSDV_DATA_AX25","r");
	}
	if(ssdv_datei != NULL)					// Solange das Bildende nicht erreicht
	{
		char_zaehler=0;
		ssdv_data[0] = 'S';
		ssdv_data[1] = 'S';
		ssdv_data[2] = 'D';
		if(ssdv_image==0)				// Unterscheidung von zwei Bildern
		{
			ssdv_data[3] = 'A';
		}
		else
		{
			ssdv_data[3] = 'B';
		}
		ssdv_data[4]=msb_packet;
		ssdv_data[5]=lsb_packet;
		char_zaehler=6;
		while(char_zaehler<SSDV_DATA_LEN)		// SSDV Array charweise gefüllt bis maximaler Länge erreicht
		{
			c=fgetc(ssdv_datei);
			ssdv_data[char_zaehler]=c;
			char_zaehler++;
			if(c==EOF)				// Ende der Datei erreicht vor maximaler Länge
			{
				break;
			}
		}
		printf("%c SSDV: Gesendet: %i \n",ssdv_data[3], char_zaehler);
		int z=0;
		printf("SSDV: ID %i -  Packet %i [%c%c]... \n",ssdv_index, ssdv_packet, msb_packet, lsb_packet);
		ssdv_packet++;
		for(z=0; z<ssdv_packet; z++)
		{
			lsb++;
			if(lsb>15)
			{
				lsb=0;
				msb++;
				if(msb>15)
					msb=0;
			}
		}
		if(lsb>9)
			charOffset=7;
		else
			charOffset=0;
		lsb_packet=lsb+48+charOffset;
		if(msb>9)
			charOffset=8;
		else
			charOffset=0;
		msb_packet=msb+48+charOffset;
		lsb=0;
		msb=0;
		if(c==EOF)
		{
			fclose(ssdv_datei);			// Schließen der Datei (wichtig für Neubeschreiben)
			ssdv_packet=0;				// Setze Zähler auf Null
			msb_packet='0';
			lsb_packet='0';
			msb=0;
			lsb=0;
			ssdv_datei=NULL;			// Setze Zeiger auf Null
			
			printf("SSDV: Übertragung beendet... \n");
			system("sudo rm SSDV_DATA_AX25");
			if(ssdv_image==0)
			{
				ssdv_image=1;
			}
			else
			{
				ssdv_image=0;
			}
			if(ssdv_index==255)
			{
				ssdv_index=0;
			}
			else
			{
				ssdv_index++;
			}
		}
		else
		{
			printf("SSDV: ID %i -  Packet gesendet... \n",ssdv_index);
		}
	}
	else
	{
		printf("SSDV: Datei nicht gefunden!\n");
	}
	return ssdv_data;
}


Radiometrix NTX2 

radiometrix ntx2

Das Radiometrix NTX2 ist ein schmalbandiges Sendemodul im 70 Zentimeter Band mit einer Ausgangleistung von 10 Milliwatt. Das Modul ist in den drei Sendefrequenzen 434.075 MHz, 434.650 MHz und 458.700 MHz erhältlich. Durch das kleinen Abmaß und den geringen Verbrauch eignet sich dieses Modul besonders gut für mobile Anwendungen. Die Verwendung des Sendemoduls ist relativ unkompliziert und erfolgt über den EN-Pin (Enable) und den TxD-Pin (Transmitter). Wird der EN-Pin auf Versorgungsspannungspotential gezogen, so werden alle am TxD-Pin anliegenden Daten als FM-Signal gesendet. Die Verbindung zur Zentraleinheit erfolgt über ein USB-TTL Converter, der es ermöglicht den USB Port des Raspberry Pi als zusätzlichen seriellen Port zu verwenden, dies ist notwendig, da die UART Schnittstelle des Raspberry Pi durch das TNC-Pi belegt wird. Das Radiometrix NTX2 wird als redundantes Sendemodul für die Übertragung von Bilddaten verwendet. Hierfür werden zunächst die Bilddaten mittels SSDV in ein Textformat konvertiert und anschließend per RTTY im Upper Side Band gesendet. Bei der Übertragung per RTTY (RadioTeleType, Funkfernschreiben) werden die einzelnen Textzeichen als serieller Bitstrom übertragen. High (Bitwert 1) und Low (Bitwert 0) werden durch zwei definierte Töne übertragen, welche sich im Abstand von einigen Hundert Hertz voneinander befinden.

Die Verbindung von Radiometrix NTX2 und Raspberry Pi kann per UART Schnittstelle der GPIOs erfolgen oder über einen der USB-Ports stattfinden. In verwendeten Aufbau wurde die Verbindung über eine der USB-Schnittstellen realisiert, die Steuerung und Übertragung der Daten erfolgt durch ein in der Programmiersprache C erzeugtes Programm.

Für die Initialisierung eine USB Uart Schnittstelle wird für die Pegelanpassung ein USB-TTL-Converter benötigt, sowie die C Bibliothek termios.h,eine UNIX API für die Ein- und Ausgabe per Terminal.

Die USB-Ports des Raspberry Pi befinden sich im Linux System unter /dev/ttyUSBx.

//InitialisierungenUSBUART
#include<termios.h>
#include<fcntl.h>
char*ntx2_port="/dev/ttyUSB0";

Bei der Initalisierung werden die Protokolleigenschaften definiert, nur wenn beide Seiten (Sender und Empfänger) gleich konfiguriert sind, ist eine Kommunikation möglich. Wichtige Eigenschaften sind die Anzahl der übertragenden Symbole pro Zeiteinheit, die Anzahl der Datenbits und Stop-Bits sowie das Paritätsbit.

//AnsteuerungUSBPort

void ntx2_init() { printf("NTX2: Initialisierung...\n"); int ntx2_fd=open(ntx2_port,O_RDWR|O_NONBLOCK|O_SYNC|O_NDELAY); // USB Serial Port Handler struct termios ntx2_option; cfsetospeed(&ntx2_option, B600); // Baudrate Output, 600 Baud cfsetispeed(&ntx2_option, B600); // Baudrate Input, 600 Baud ntx2_option.c_cflag &= ~PARENB; // kein Paritätsbit ntx2_option.c_cflag &= ~CSTOPB; // ein Stop-Bit ntx2_option.c_cflag &= ~CSIZE; // Dateneinstellungen löschen ntx2_option.c_cflag |= CS8; // 8 Datenbits ntx2_option.c_cflag &= ~CRTSCTS; // Hardwareflow deaktiveren tcsetattr(ntx2_fd, TCSANOW, &ntx2_option); // setze USB Uart Einstellungen }

 

Ist die Schnittstelle einmal konfiguriert, können nun Daten übertragen werden. Die Baudrate bestimmt dabei die Übertragungsgeschwindigkeit. Wir verwenden das Radiometrix NTX2 als Sendemodul für die Bilddaten. Die Bilddaten werden zunächst in das SSDV Format konvertiert und liegend anschließend als Textformat vor. Das Bild wird in n-Segmente zerlegt, wobei jedes Segment aus genau 256 Zeichen (chars) besteht. Diese einzelnen Segmente werden in zwei Teilpaketen mit je 128 Zeichen versendet. Hierfür wird ein Array usb_rtty mit 128 Zeichen gefüllt und anschließend in den Handler des USB-Ports geschrieben. In seltenen Fällen kann es beim Beschreiben des USB-Ports zu Fehlern kommen, in diesen Fällen wird der USB-Port geschlossen und anschließend neu initialisiert.

 
void ntx2_send_data(uint8_t* send_data)
{
	// Senden per USB UART als RTTY (under progress
	uint8_t char_rtty;
	int i;
	for(i=0; i<128; i++)				// Fülle das Datenarray
	{
		char_rtty=*(send_data+i);
		usb_rtty[i]=char_rtty;
	}
	write(ntx2_fd, usb_rtty, 128);			// Sende Daten über USB Uart NTX2
	if(tcdrain(ntx2_fd)!=0)				// Sende Daten über USB fehlgeschlagen 
	{
		ntx2_close();			// Port schließen
		sleep(4);
		ntx2_init();				// Port neu initialisieren
	}
}

 

Die Deinitalisierung erfolgt einfach über das Schließen des Handlers ntx2_fd.

 

void ntx2_close()
{
	close(ntx2_fd);
	printf("NTX2: USB Uart Port geschlossen...\n");
}

 

Mit dem Schreiben der Daten an den USB Port gelangen diese über den USB-TTL Converter an das Radiometrix NTX2. Das Sendemodul wandelt die anliegenden Daten in ein frequenzmoduliertes Signal um und sendet dieses im 434.650 MHz Band.

Das Funksignal empfangen wird an der Bodenstation über den Kenwood TS-2000 Transceiver emfangen und per Audioausgang in einen Rechner gespeist. Die einst binären Daten liegen nun als zwei definierte Tonfrequenzen vor, dieses Verfahren wird als RTTY bezeichnet. Das Dekodieren dieser erfolgt mit Hilfe der Software Fldigi, eine Soundkarten Digimodeprogramm.

Zur Entschlüsselung der Audiodaten muss die Software, nach Auswahl der Soundkarte, konfiguriert werden. Entscheidend ist, dass die, bei der Initialisierung verwendeten Protokolleigenschaften, auch hier mit übernommen werden. Unter OP-Mode -> RTTY -> Custom übernehmen wir die vorher bestimmten Eigenschaften für Baudrate, Parity und Stop-Bit. Je nach eigensetztem Funkmodul ist die Shift-Frequenz, der Abstand der beiden Töne für das High Bit (1) und Low Bit (0) unterschiedlich. Zur Ermittelung der Shift-Frequenz wurde ein einfaches Programm verwendet, welches abwechselnd ein High und Low-Signal aussendet. 

fldigi

Über die konfigurierte Software Fldigi werden die Audiopegel in binäre Muster und von diesen Bitmustern in Zeichen zurückgewandelt. Aus den Zeichen lässt sich nun das im SSDV codierte Bild zurückgewinnen, dies geschieht vollautomatisch, sobald ein vollständiger SSDV Datensatz übertragen wurde. Das dekodierte Bild lässt sich unter View -> SSDV RX betrachten.

Raspberry Pi Bilder

Während des Fluges dokumentiert der Raspberry Pi über die Raspberry Pi Kamera den Flug indem dieser kontinuierlich Bilder aufnimmt. Die aufgenommenen Bilder werden zunächst ausgewertet, verkleinert, in das SSDV Format überführt und anschließend per Funk an die Bodenstation übertragen. Um diese Aufgabe zu automatisieren wurde ein Programm geschrieben, welche auf den SSDV Quellcode von Philip Heron zugreift.

Das Programm nimmt im ersten Schritt drei Bilder mit den Namen ssdv1, ssdv2 und ssdv3 in maximaler Auflösung auf und berechnet die Dateigröße der einzelnen Bilder. Da die Bilder in etwa die gleiche Umgebung betrachten, ist in der Theorie das Bild mit der größten Dateigröße auch das schärfste Bild. Die hängt damit zusammen, dass bei unscharfen oder verwackelten Bilder die Kontraste geringer ausfallen und eine Komprimierung in das JPG Format größere Bereiche zusammenfassen kann, hierbei lässt sich eine bessere Komprimierung durchführen und die Dateigröße verringern. Ein scharfes Bild besitzt mehr Kontraste und demnach auch eine geringere Kompression. Als Auswahlkriterium lässt sich dieses Verfahren jedoch nur verwenden, wenn der Bildausschnitt zwischen den drei Aufnahmen relativ ähnlich ist.

system("sudo raspistill -o ssdv1 -w 2592 -h 1944");		// Drei Bilder aufnehmen
system("sudo raspistill -o ssdv2 -w 2592 -h 1944");
system("sudo raspistill -o ssdv3 -w 2592 -h 1944");
FILE *ssdv_ptr=NULL;
int size=0;				// Größe eines Bildes
int maxsize=0;				// Größe größtes Bild
char ssdv_name[6]="ssdv0\0";		// Speichername Bilder
char ssdv_picture[6]="ssdv0\0";		// Speichername größtes Bild
char ntx_picture[9]="ssdv_ntx\0";		// Zu sendendes Bild skaliert auf 320x240 Pixel
int i;
printf("Aufgenommene Bilder | ");
for(i=0;i<3;i++)
{
	ssdv_name[4]=i+49;			// Erzeuge fortlaufende Bildnamen
	ssdv_ptr = fopen(ssdv_name,"rb");	// Öffne Bild
	fseek(ssdv_ptr,0,SEEK_END);		// Zähle Bildgröße
	size=ftell(ssdv_ptr);
	fclose(ssdv_ptr);			// Schließe Bild
	ssdv_ptr=NULL;
	printf("%s : %i bytes |",ssdv_name, size);
	if(i==0)				// Suche größtes Bild
	{
		maxsize=size;
		ssdv_picture[4]=49;
	}
	else
	{
		if(maxsize<size)
		{
			maxsize=size;
			ssdv_picture[4]=i+49;
		}
	}
}
printf("Größtes Bild: %s \n", ssdv_picture);

 

Das beste Bild wird in einem Archivordner gesichert und auf einere geringere Auflösung skaliert. Die Skalierug ist notwendig um eine möglichst schnelle Bildrate zu erzielen. Die Sicherung im Archivordner erfolgt in voller Auflösung und ermöglicht eine Betrachtung nach der Ladung.

/ Skaliere Bild zum versenden auf 320x240 Pixel
char scale[44]="sudo convert ssdv0 -resize 320x240 ssdv_ntx\0";		// Skaliere Bild auf 320x240 mittel Imagemagick convert
scale[17]=ssdv_picture[4];
system(scale);
char move[42]="sudo cp ssdv0 ../skylab/SSDV_PIC/ssdv000\0";		// Verschiebe Kopie an Speicherort
move[12]=ssdv_picture[4];
int k=0;
int l=0;
int m=0;
for (i=0;i<ssdv_index;i++)						// Bilder durchnummeriert ablegen
{
	k++;
	if(k>9)
	{
		k=0;
		l++;
		if(l>9)
		{
			l=0;
			m++;
			if(m>0)
			{
				m=0;
			}
		}
	}
}
move[37]=48+m;
move[38]=48+l;
move[39]=48+k;
system(move);

 

Das skalierte Bild kann nun in das SSDV Format konvertiert werden.

// Codieren Bild nach SSDV --> basierend auf Philip Herons SSDV Open Source Projekt
int c;
FILE *fin=NULL;
FILE *fout=NULL;
fin=fopen(ntx_picture,"rb");
fout=fopen("SSDV_DATA","wb");
char type = SSDV_TYPE_NORMAL;
char callsign[6]="DN4GB ";
ssdv_t ssdv;
uint8_t pkt[SSDV_PKT_SIZE], b[128];
ssdv_enc_init(&ssdv, type, callsign, ssdv_index);	// Header vorbereiten (Rufzeichen, Nummer)
ssdv_enc_set_buffer(&ssdv, pkt);			// Buffer vorbereiten
while(1)
{
	while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME)
	{
		size_t r = fread(b, 1, 128, fin);	// lese zu kodierendes Bild ein
       		ssdv_enc_feed(&ssdv, b, r);		// codiere Bilddaten nach SSDV
	}
	if(c==SSDV_EOI)
	{
		break;			// Datei vollständig umgewandelt
	}
	fwrite(pkt, 1, SSDV_PKT_SIZE, fout);	// schreibe codierete Daten in Datei
}
fclose(fin);
fclose(fout);

 

Die Datei ntx_picture enthält das skalierte Bild im SSDV Format und kann Zeichenweise ausgelesen und versendet werden. Das Versenden erfolgt ebenfalls automatisiert mittels des gleichen Programmes. Das Programm überprüft im Vorfeld ob bereits ein kodiertes Bild vorliegt und erzeugt eine Zeichenkette aus einem Header und 128 Zeichen des codierten Bildes. Innerhalb einer Schleife werden je 128 Zeichen des SSDV codierten Bildes ausgelesen und versendet, bis der gesamte Inhalt gelesen wurde.

// Codieren Bild nach SSDV --> basierend auf Philip Herons SSDV Open Source Projekt
int c;
FILE *fin=NULL;
FILE *fout=NULL;
fin=fopen(ntx_picture,"rb");
fout=fopen("SSDV_DATA","wb");
char type = SSDV_TYPE_NORMAL;
char callsign[6]="DN4GB ";
ssdv_t ssdv;
uint8_t pkt[SSDV_PKT_SIZE], b[128];
ssdv_enc_init(&ssdv, type, callsign, ssdv_index);	// Header vorbereiten (Rufzeichen, Nummer)
ssdv_enc_set_buffer(&ssdv, pkt);			// Buffer vorbereiten
while(1)
{
	while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME)
	{
		size_t r = fread(b, 1, 128, fin);	// lese zu kodierendes Bild ein
       		ssdv_enc_feed(&ssdv, b, r);		// codiere Bilddaten nach SSDV
	}
	if(c==SSDV_EOI)
	{
		break;			// Datei vollständig umgewandelt
	}
	fwrite(pkt, 1, SSDV_PKT_SIZE, fout);	// schreibe codierete Daten in Datei
}
fclose(fin);
fclose(fout);

 

Die Datei ntx_picture enthält das skalierte Bild im SSDV Format und kann Zeichenweise ausgelesen und versendet werden. Das Versenden erfolgt ebenfalls automatisiert mittels des gleichen Programmes. Das Programm überprüft im Vorfeld ob bereits ein kodiertes Bild vorliegt und erzeugt eine Zeichenkette aus einem Header und 128 Zeichen des codierten Bildes. Innerhalb einer Schleife werden je 128 Zeichen des SSDV codierten Bildes ausgelesen und versendet, bis der gesamte Inhalt gelesen wurde.

 

uint8_t* ssdv_get_data()
{
	static uint8_t ssdv_data[SSDV_DATA_LEN];		// SSDV Datenarray pro Übertragung
	int char_zaehler;					// Zähler für Größe des Sendearrays
	int c;							// byteweises einlesen der codierten Bilddaten
	if(ssdv_packet==0)					// Falls neue Übertragung dekodiere Bild und öffne Datei
	{
		ssdv_encode_picture();
		ssdv_datei=fopen ("SSDV_DATA","r");
	}
	if(ssdv_datei != NULL)					// Solange das Bildende nicht erreicht
	{
		char_zaehler=0;
		ssdv_data[0] = 'S';
		ssdv_data[1] = 'S';
		ssdv_data[2] = 'D';
		ssdv_data[3] = 'V';
		char_zaehler=4;
		while(char_zaehler<SSDV_DATA_LEN)		// SSDV Array charweise gefüllt bis maximaler Länge erreicht
		{
			c=fgetc(ssdv_datei);
			ssdv_data[char_zaehler]=c;
			char_zaehler++;
//			printf("%i ", c);
			if(c==EOF)				// Ende der Datei erreicht vor maximaler Länge
			{
				break;
			}
		}
//		printf("\n");
		ssdv_packet++;
		if(c==EOF)
		{
			fclose(ssdv_datei);			// Schließen der Datei (wichtig für Neubeschreiben)
			ssdv_packet=0;				// Setze Zähler auf Null
			ssdv_datei=NULL;			// Setze Zeiger auf Null
			printf("SSDV: Übertragung beendet... \n");
			system("sudo rm SSDV_DATA");
			if(ssdv_index==255)
			{
				ssdv_index=0;
			}
			else
			{
				ssdv_index++;;
			}
		}
		else
		{
			printf("SSDV: ID %i -  Packet %i... \n",ssdv_index, ssdv_packet);
		}
	}
	else
	{
		printf("SSDV: Datei nicht gefunden!\n");
	}
	return ssdv_data;
}

 

Sobald der gesamte Inhalt versendet wurde, werden alle Dateien gelöscht und der Raspberry für die Aufnahme der nächsten drei Bilder vorbereitet.

 

Die Bodenstation

Kenwood TS-2000kenwood

Der Kenwood TS-2000 Transceiver ist ein Multiband Receiver im KW/50/144/430 MHz Band, der den gleichzeitigen Empfang von zwei Bändern ermöglicht, das Sub-Band ist jedoch auf 144/433 MHz AM/FM limitiert. Für den Empfang des NTX2 Sendemoduls wird das Main-Band verwendet, da nur hierüber der Empfang im Upper Side Band möglich ist. (Frequenz: 434,650MHz USB).

Das Sub-Band des Transceivers wird für den Empfang des Baofeng UV5-R eingesetzt. Für die Demodulation der Daten wird ein externer Terminal Node Controller (TNC) eingesetzt. Der Anschluss eines TNCs an das Sub-Band ist nicht mittels des mitgelieferten ACC Kabels möglich, da die benötigten Pole nicht verbunden sind, daher wurde eine entsprechendes Kabel mit Verdrahtung für das Sub-Band gefertigt.

kabel

SCS PTC-IIex

scsptc

Das PTC-IIex Modem wird als externes TNC verwendet und dient zur Übertragung der empfangenen Daten des Baofeng UV-5R an einen Rechner. Hierfür werden die nach Demodulation erzeugten, Audiosignale des Transceivers in TTL-Pegel umgewandelt und anschließend über eine RS232-Schnittstelle bereitgestellt. Die so übertragenen Daten gelangen über eine USB-RS232 Schnittstelle an einen Rechner. Für die Übertragung der Daten an einen Rechner wird das SCS PTC-IIex als KISS Modem verwendet. Dies ermöglicht eine einfache Kommunikation von Rechner zu Modem, erfordert aber im Gegenzug eine eigene Aufschlüsselung der empfangenen Daten, in diesem Fall die Interpretation des AX25 Protokolls.

 

Aufbau des Übertragungsprotokolls

 

Das Übertragungsprotokoll zwischen Rechner und der Basisstation setzt sich aus KISS, AX25 und eigenem Header zusammen.

Protokolle

Das KISS Protokoll besteht aus den vier Steuerzeichen

FEND (0xC0) – Frame End

FESC (0xDB) – Frame Escape

TFEND (0xDC) – Transposed Frame End

TFESC (0xDD) – Transposed Frame Escape

Das Frame End Zeichen symbolisiert den Start einer Nachricht gefolgt von der Portnummer im Hex-Format, anschließend folgen n-Zeichen mit der zu übertragenden Information, welche durch ein weiteres Frame End abgeschlossen wird.

Zu beachten gilt, dass das Frame End Zeichen auch Bestandteil der zu übermittelnden Nachricht sein kann. In diesem Fall ersetzt das KISS Protokoll dieses durch die Kombination aus FESC und TFEND, ebenso wir ein Frame Escape durch die Kombination aus FESC TFESC ersetzt. Die Erkennung und Rücktransformation dieser muss durch entsprechenden Code innerhalb der Basisstation abgefangen werden.

Innerhalb des KISS Protokolls finden wir die AX25 Pakete, die mittels des Raspberry Pi versendet wurden. Das AX25 Protokoll beginnt mit einem acht Bit breitem Bitmuster (Flag), gefolgt von einer 112 Bit breiten Adresse (bestehend aus 7 Byte Ziel und Startadresse), sowie einer Kontrollinfo von acht Bit Breite. Die zu übertragende Nachricht beträgt n*8 bit und endet mit einem 16bit Prüfsummencode sowie dem acht Bit Breitem Bitmuster.

Für die Unterscheidung von Nachrichten der Sonde und Fremdnachrichten auf gleicher Frequenz wird innerhalb der Bodenstation der AX25 Header, bestehend aus Flag, Adresse und Kontrollinfo, aller eingehenden Nachrichten überprüft.

Die aus dem AX25 Protokoll gewonnenen Daten besitzen zur Unterscheidung von Mess- und Bilddaten noch den vom Raspberry Pi erzeugten Header. Die daraus gewonnenen Daten werden innerhalb der Basisstation in einer SQL Datenbank gesichert und durch die Grafikklasse Drawinci visualisiert.

 

 

 

 

 

Software Bodenstation

Die Software der Bodenstation ist mit  Qt5 programmiert. Auf Grundlage der Programmiersprache C++ steht für die Gestaltung einer grafischen Benutzeroberfläche oder einer grafischen Ausgabe eine Reihe von Bibliotheken zur Verfügung. Qt5 wurde durch das Unternehmen Trolltech geschaffen und 2008 von Nokia aufgekauft. 2011 wurde das Projekt als Qt-Projekt zu open source Software erklärt und frei verfügbar gemacht. Die Qt-Bibliothek wird für Windows, Linux und Mac Systeme angeboten und unter drei Lizenzmodellen vertrieben. Eine kommerzielle Nutzung ist ebenfalls ohne Lizenzkosten möglich, sofern man die Rahmenbedingungen der gewählten Lizenz berücksichtigt. Die drei Lizenzmodelle sind das GPL, LGPL sowie eine reine kommerzielle Lizenz.

Das folgende Blockdiagramm beschreibt den Aufbau der Software:

DrawinciBlockDiagramm

DIe Software kommuniziert über eine serielle Schnittstelle (USB/RS232) mit dem PTC-IIex Modem (TNC). Das Übertragungsprotokoll wird in verschiedenen Instanzen dekodiert, um aus dem  KISS-Protokoll die emfangenen AX25-Pakete zu dekodieren. In der Payload der AX25-Pakete wird der Header des Bild und Messdatenprotokolls (BMP) zur Unterscheidung der übermittelten Daten verwendet. Die SSDV-Bilddaten werden über die SSDV Software von Phillip Heron dekodiert. Sensordaten werden ebenfalls dekodiert und in der SQLite-Datenbank Drawinci-konform abgelegt. Die Benutzeroberfläche bereitet die Messdaten grafisch anspruchsvoll auf. Die Bilder werden paketweise grafisch aufgebaut und aktuallisiert. Eine Konsolenansicht gibt detaillierte Informationen zu Übertragungsstati von Sensor und Bilddaten.

 

Drawinci Graphic API

Für die Visualisierung der Daten wurde mit Hilfe des Qt-Frameworks eine Grafikklasse realisiert. Die Grafikklasse ist als plattformunabhängiges, skalierbares, objektorientiertes Programm konzipiert und ermöglicht es beliebig viele Graphen in beliebig vielen Fenstern darzustellen. Als Darstellungsart kommen neben Balken-, Säulen-, XY-, Flächen-, Linien- oder Kreisdiagrammen noch eine Vielzahl weitere Darstellungsformen in Betracht. Die einzelnen Darstellungsarten lassen sich beliebig kombinieren und auf unterschiedlichen Achsen skaliert abbilden. Neben der Darstellung über einen PC ist auch die Verwendung als Smartphonelösung oder über den Raspberry Pi möglich.

Durch die breitgefächerten Konfigurationsmöglichkeiten ergibt sich eine flexible Grafiklösung, die auch außerhalb des Stratosphärenballons Verwendung findet.

 

 

Drawinci

Nützliche Links:

http://k4gbb.us/docs/Raspberry.html

http://tnc-x.com/TNCPi.pdf

http://ukhas.org.uk/guides:ssdv

http://manpages.ubuntu.com/manpages/quantal/man8/beacon.8.html

www.scs-ptc.com/

http://www.ka9q.net/papers/kiss.html