Visserslatijn

5 mei 2016

GPS disciplined OCXO

Filed under: Geen categorie — visserslatijn @ 21:17

Een hele tijd geleden heb ik eens mee gedaan met zo’n crowdfunding actie voor een GPS dingetje (NavSpark GL). Daar heb ik eigenlijk nooit iets mee gedaan. Nou was ik een poosje bezig met een OCXO die ik heb gecalibreerd met mijn Rubidium standaard, wat overigens heel goed werkt.

In die tijd kwam ik zo nu en dan zelfbouw projecten tegen die de OCXO continu afstemden met behulp van een GPS. Dat wilde ik ook eens proberen. Ik heb het ding weer opgezocht en aangesloten. Daar komt een hele mooie puls uit van precies 1Hz. Heel precies volgens de docs, maximale jitter iets van 10nS.

navspark_gl

Behalve die 1puls per seconde (1PPS) komt er natuurlijk ook GPS data uit in de vorm van een 115200 baud stream van NMEA strings. Daar doe ik nog weinig mee, ik gebruik het om de tijd in UTC, de datum, het aantal zichtbare satellieten op een scherm te zetten. Ik was eerst van plan om van die data het aantal satellieten te gebruiken als signaal dat je de output van de oscillator mag vertrouwen. Dat bleek later niet nodig. De NMEA data wordt dus niet echt gebruikt. Al heb ik er wel een heel nauwkeurige klok erbij.

De OCXO die ik heb liggen levert een merkwaardige frequentie van 38,880,000 Hz. Dat is volgens Wikipeadia een frequentie die door de telecommunicatie maatschappijen werd gebruikt voor hun onderlinge communicatie. Niet interessant genoeg om daar veel tijd aan te besteden. En omdat het zo’n “onhandige”  frequentie is zijn ze lekker goedkoop via ebay te koop.

ocxo

Pas laat kwam ik er achter dat je deze dingen over een erg groot bereik kunt tunen, hoeveel precies weet ik niet meer maar het zijn honderden Herz boven en onder die 38.88MHz. Waarom zoveel is me een raadsel en het leverde me ook problemen op, waarover later meer. Je stemt de oscillator af met een regelspanning op de EFC ingang.

De 1PPS puls gaat een 74HC390 (dual decade counter) in die ik zo gebruik dat er een signaal van 8 seconden laag en 2 seconden hoog uit komt.

Dat signaal is de CLK-enable van een teller die als ingangsignaal  de OCXO heeft en dus 8 seconden lang 38,880,000 pulsen telt. Dat zijn er 311,040,000.

De microcontroller is een ATMEGA8 die niet in staat is om 38MHz signalen te verwerken, daarnaast wilde ik de counters van de controller helemaal niet gebruiken omdat het in en uitschakelen altijd gesynchroniseerd wordt met de clock van de microcontroller, ook als de counter een externe clock heeft. Dat zou al snel enkele microseconden fout opleveren.

De counter is dus ook in hardware uitgevoerd. Eerst wilde ik een enkele 74HC590 (8 bit binairy counter) gebruiken. Dat klinkt op het eerste gezicht wat vreemd, die kan immers maar van 0 tot 255 tellen en er komen meer dan 300 miljoen pulsen voorbij. Maar het zijn er altijd ongeveer evenveel. Zouden het er precies 311,040,000 zijn dan is de teller 1,215,000 keer rond gegaan en staat deze dus op 0. Als het er een paar meer waren komt de teller iets hoger te staan en waren het iets minder dan staat de teller nog voor de 255. Met een signed integer van 8 bits krijg je dan mooi een getal dat het tekort of overschot aan pulsen voorstelt.

Helaas werkte dat niet goed. En dat komt doordat de OCXO, zoals ik al eerder zei, over een groot bereik getuned kan worden. De afwijking van gewenste aantal pulsen kan makkelijk meer dan plus of min 128 worden. De oplossing was dus twee tellers te gebruiken, een extra investering van 25 eurocent. Dat is geen probleem, ik maak tenslotte maar een (1) apparaat, geen hele serie. Maar dit leverde een ander, groter, probleem voor me op. Zie hier het schema:

20160505_174118

De  1PPS die door tien gedeeld wordt en een duty cycle van 80% heeft enabled de clock van de tellers en gaat ook naar de INT0 ingang van de ATMEGA8. Zodra de tellers stil staan nadat ze 8 seconden hebben geteld wordt er een interrupt gegenereerd en zijn er 2 seconden de tijd om de data te verwerken. De tellers zetten de data op de uitgangen met behulp van RCK en kunnen daarna gereset worden. Dan leest de microcontroller de tellerstanden en gaat daarmee aan het werk. Nu was het plan om met een enkele 8 bit counter te werken, er bleven dan voldoende GPIO poorten over voor andere dingen, zoals een LCD schermpje. Dat ging dus niet door, alle GPIO poorten zijn in gebruik. Er moet zelfs gebruik gemaakt worden van de interne 8MHz rc oscillator, zo weinig poorten waren er over. Eigenlijk zou ik over moeten stappen op een andere controller… Maar goed, nu mag de tellerstand wel 32000 te hoog of te laag zijn. Zo groot is het tunings bereik van de OCXO niet. Jammer is wel dat als de juiste frequentie bereikt is de teller niet mooi op nul uitkomt maar op 6144.

Het afstemmen van de OCXO gaat zoals gezegt met een spanning tussen de 0 en 5 volt, na meten bleek de oscillator zo rond de 1.25v op de juiste frequentie te staan. Dus met een regelbare spanning tussen de 0 en 2.5v is deze mooi af te stemmen. Hiervoor wordt TIMER1 gebruikt die een PWM signaal maakt  in 32768 stapjes, dat leek me wel nauwkeurig genoeg, stapjes van 76 uV. En de PWM frequentie is zo ook nog hoog genoeg (244 Hz) De 47u condensator is later vervangen door eentje van 220u wat een lowpass filter met een cutoff van 0.05Hz geeft. Dit gaat direct de OCXO in, die is hoogohmig genoeg.

Hieronder het programma voor de aansturing van de OCXO,  in een volgende post de uitleg.

#define F_CPU 8000000UL
#define UART_BAUD_RATE 9600
#define CLR_74HC590 PB0
#define RCK_74HC590 PB2
#define REMAINDER 6144
#define PWM_START 0x41cd
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include "uart.h"

volatile int16_t old_pwm[8];

int main(void)
{
 OSCCAL = 0x9c;
 DDRB = 0x07; //PB0,1,2 output
 DDRC = 0x00; //all input
 DDRD = 0x00; //all input (uart does its own init)

 TCCR1A |= (1 << COM1A1) | (1 << COM1A0); //SET OC1A on Compare Match, CLEAR OC1A at BOTTOM
 TCCR1A |= (1 << WGM11); //fast pwm, top = ICR1
 TCCR1B |= (1 << WGM13) | (1 << WGM12); //fast pwm
 TCCR1B |= (1 << CS10); //prescaler = 1
 ICR1 = 0x7FFF;
 OCR1A = PWM_START; //pwm starting value
 
 MCUCR |= (1 << ISC01) | (1 << ISC00); //INT0 on rising edge
 GIMSK |= (1 << INT0); //interrupt INT0 enabled
 
 uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
 uart_flush();
 
 for (uint8_t x=0; x<8; x++) //fill array with wanted settings
 {
   old_pwm[x] = PWM_START;
 }
 
 sei();
 
 while (1)
 {
   //nothing, all work is done in the interrupt routine
 }
}

ISR(INT0_vect) //once every 10 seconds, after 8 seconds of counting
{ //we have two seconds here, so no hurry
 uint16_t stepsize = 32;
 uint8_t freq_low, freq_high;
 int16_t frequency = REMAINDER;
 static uint32_t pwm_value = PWM_START;
 static uint8_t x = 0;
 uint8_t y = 0;
 char buffer[7];

 PORTB |= (1 << RCK_74HC590);
 _delay_ms(1);
 PORTB &= ~(1 << RCK_74HC590);
 _delay_ms(1);
 PORTB &= ~(1 << CLR_74HC590);
 _delay_ms(1);
 PORTB |= (1 << CLR_74HC590);
 _delay_ms(1);
 
 freq_low = (PINC & 0x3F) | (PIND & 0xC0);
 freq_high = ((PINB & 0xF8) >> 3) | ((PIND & 0x38) << 2);
 frequency = (freq_high << 8) | freq_low;
 
 if ((frequency - REMAINDER) > 2)
 {
 if ((frequency - REMAINDER) >= 15) stepsize = 256;
 if ((frequency - REMAINDER) < 15) stepsize = 16;
 if ((frequency - REMAINDER) < 10) stepsize = 4;
 if ((frequency - REMAINDER) < 5) stepsize = 1;
 if ((frequency - REMAINDER) < 3) stepsize = 0;
 pwm_value += stepsize;
 }
 
 if ((REMAINDER - frequency) > 2)
 {
 if ((REMAINDER - frequency) >= 15) stepsize = 256;
 if ((REMAINDER - frequency) < 15) stepsize = 16;
 if ((REMAINDER - frequency) < 10) stepsize = 4;
 if ((REMAINDER - frequency) < 5) stepsize = 1;
 if ((REMAINDER - frequency) < 3) stepsize = 0;
 pwm_value -= stepsize;
 }
 
 uart_puts("FRQ");
 ltoa(frequency, buffer, 10);
 uart_puts(buffer);
 uart_puts("\n\r");
 
 uart_puts("PWM");
 ltoa(pwm_value, buffer, 16);
 uart_puts(buffer);
 uart_puts("\n\r");
 
 old_pwm[x] = pwm_value;
 if (x < 7) x++;
 else x = 0;
 
 pwm_value = 0;
 for (y=0; y<8; y++)
 {
   pwm_value += old_pwm[y];
 }
 pwm_value = pwm_value / 8;
 
 OCR1A = (uint16_t) pwm_value;
 
 uart_flush();
}

 

 

18 januari 2016

DIY_DDS

Filed under: Geen categorie — visserslatijn @ 20:56

Een signaal generator is altijd makkelijk om er bij te hebben. Dertig jaar geleden (… ik wordt oud) had ik er een gemaakt met een XR2206. Dezelfde ontwerpen vind je trouwens nu nog als bouwpakketje of als kant een klaar printje op diverse plaatsen. Zoek maar eens op Ebay.

Van dat zelfde Ebay had ik enige tijd geleden een AD9851 module gekocht en er ook een signaal generator mee gemaakt.

AD9851

Daar komt een mooie 1 Volt tt sinus uit van 1Hz tot 10MHz, ja hij kan hoger maar dat heb ik toch nooit nodig. Het ding heeft een klokfrequentie van 180MHz en als er per 2 klokcycli een sample van de sinus gemaakt wordt zijn er bij 10 MHz nog krap 9 over. Dat zal al geen mooie sinus meer zijn. Het apparaat is nog niet af, geen uitgangsbuffer, geen volume regeling en geen offset. De output is nu een sinus van 1 Volt tt met een vaste offset van 0.5 Volt. Niet altijd even practisch.

Maar het is leuker om zelf een signaal generator te maken, uiteraard ook met DDS. Die kan dan mooi die oude XR2206 vervangen. Het maken van een DDS blijkt verrassend eenvoudig, de slimmigheid zit ‘m in de phase-accumulator. Ik heb ‘m gemaakt met een ATMEGA328 op 16MHz. Met een timer laat ik 100.000 interrupts per seconde genereren die dus minder dan 160 klokcycli lang mogen duren. En dan blijft er niets over voor de rest van het programma, die de rotary encoder, een drukknopje en het LCD scherm nog moet aansturen.

Als eerste een R2R ladder netwerkje gemaakt met 1k en 2k weerstanden, vrij lage waarden maar het blijkt goed te werken. Omdat de phase-accumulator routine heel regelmatig moet functioneren laat ik de rotary encoder een eenmalige interrupt genereren die vervolgens een timer inschakeld voor het debouncen van de encoder (die zijn echt heel slecht). Na ongeveer een kwart seconde gaan alle verstorende interrupts weer uit en blijft alleen die van de phase-accumulator over. Je kan zien dat er kleine onregelmatigheden in het signaal zitten als je aan de knoppen zit, maar zolang je er van af blijft is het signaal mooi schoon. Al zitten er nog wel problemen aan het maken van signalen met een dds, waar ik later op terug zal komen. (TODO: jitter)

Zie hier de schema’s

DIY_DDS_GENERATOR

 

DIY_DDS_FILTER

 

Als microcontroler gebruik ik een Arduino Pro Mini die gewist is en direct vanuit Atmel Studio geprogrammeerd wordt het is dus geen Arduino meer. De software van Arduino maakt het ding veel te traag en ik mis de controle over de interrupts.

Hier de opbouw op een breadboard. (links de generator, rechts het filter)

DIY_DDS_01

Ik had flink problemen met het kiezen van de juiste poorten op de ATMEGA328. Het mooiste is natuurlijk als je de juiste waarde voor het volgende sample van de golfvorm in één keer op de uitgang kan zetten, maar helaas het mocht niet zo zijn. PORTB kon ik niet gebruiken omdat daar het kristal al op is aangesloten, en ik wilde natuurlijk wel kristal stabiliteit en een “hoge” klok frequentie. De interne klok is 8MHz en niet nauwkeurig en ook temperatuur afhankelijk. PORTC viel ook af omdat die maar zes bits breed is, PC6 is de RESET en PC7 werkt om een of andere reden ook niet. En helaas kon ik ook niet alle 8 bits van PORTD gebruiken omdat ik gebruik wil maken van de INT0 (externe) interrupt.

Er zat dus niets anders op dan de data in twee nibbles via PORTC en PORTD naar buiten te sturen. Achteraf blijkt dat geen probleem te zijn, de glitches zijn klein en zeer kort zodat het filter ze volledig verwijderd.  Hieronder de hele interrupt voor het genereren van de golfvormen:


//interrupt voor het genereren van de golfvorm, 100k / seconde
//omdat er erg weinig tijd tussen de interrupts zit geen andere dingen uitvoeren
//als er wel andere interrupts worden uitgevoerd is de golfvorm niet meer zuiver.
ISR(TIMER0_COMPA_vect)
{
static uint32_t counter = 0;
static uint8_t output = 0;
//step defines the frequency (42950 --> 1 Hz, 429500 --> 10Hz, etc.)
counter += stepsize;
switch (waveform)
{
case 1:
output = sine[counter >> 24];
break;
case 2:
output = triangle[counter >> 24];
break;
case 3:
output = sawtooth[counter >> 24];
break;
case 4:
output = squarewave[counter >> 24];
break;
case 5:
output = sinxdivx[counter >> 24];
break;
default:
break;
}
//door een tekort aan ports de output verdelen over PORTC en PORTD
//lower nibble van PORTD is ingangen, pull-up weerstanden ingeschakeld laten
PORTC = output & 0x0F;
PORTD = (output & 0xF0) | (0x0F);
}

Het gebruik van “switch – case” bleek sneller te werken dan een lijst van “if – else”. Weten we dat ook weer :-) En zoals je ziet, wordt er gebruik gemaakt van vijf lookup tabellen voor de verschillende golfvormen.

Om ervoor te zorgen dat bovenstaande routine niet gestoord wordt staat er maar één andere interrupt aan die een neergaande flank van de rotary encoder detecteert, de INT0 interrupt doet niets anders dan een andere timer-interrupt inschakelen die naar de stapjes van de encoder luistert, zichzelf na een kwart seconde uit zet en de externe interrupt (INT0) weer inschakelt.

Hier de output van de generator op 1 kHz, mooi he!

DIY_DDS_07

Helaas bleek het niet allemaal zo mooi te zijn. Ik heb er de tabellen voor een sinus, driehoek, zaagtand, blokgolf en iets dat sin(x)/x heet in geladen. De sinus, driehoek en sin(x)/x zijn goed tot redelijk, ook op de hoogste frequentie (10kHz). Maar de zaagtand en blokgolf hebben vreselijk last van jitter ook wel dds-phase-noise genoemd geloof ik. Na een poos er over zitten na denken kan het ook niet anders dan een rommelig signaal geven. Zeker bij de hogere frequenties wordt het aantal samples uit de lookuptabel erg klein, bij 10kHz nog maar 10 en het is natuurlijk heel goed mogelijk dat de ene keer een andere waarde uit de tabel wordt gekozen dna de volgende keer. Bij een blokgolf en een zaagtand is dat desastreus! Daar is er namelijk een moment dat de waarde van 0 naar 255 (en andersom) gaat. Als je die de ene keer 10uS eerder of 10 uS later treft heb je een flinke jitter te pakken. Bij een sinus gaat het allemaal veel geleidelijker zodat de jitter er in principe wel is maar nauwelijks zichtbaar is (en door het filter nog minder wordt?)

Dus maak ik nu een TTL blokgolf door de sinus door een schmitt trigger te halen. De blokgolf uit de DDS laat ik er wel in zitten, net als de zaagtand, ik heb toch geen idee wat ik er mee moet doen :-).

Nog een paar plaatjes:

Deze zijn allemaal gemaakt op 1 kHz, op de maximale frequentie van 9,999.99 Hz ziet het er allemaal wat minder mooi uit. En wat ik met die sin(x)/x moet weet ik ook nog steeds niet.

Wat heel goed gaat met een DDS is heel lage frequenties maken. Ik ben niet verder gegaan dan 10mHz (milli, niet Mega). Je moet er even geduld voor hebben maar dan heb je ook een mooie sinus op 0.01 Hz. Ook daarvan weet ik echt niet waar het ooit goed voor is.

DIY_DDS_14

En daarna moet het allemaal nog in een kastje, de oude XR2206 er uitgesloopt, de voor en achterkant verwisseld en een stukje pertinax  (jawel dat heb ik ook nog) op de achterkant gelijmd. En dan volgt het voor mij altijd erg moeilijke deel, een indeling maken voor het frontpaneel en er een beetje netjes gaten in maken.

Inmiddels zit er nog een gaatje extra in het front waar ik de TTL blokgalf naar buiten laat komen. Klaar.

Nu eens gaan kijken naar die HF signaal generator met de AD9851, dat is voor mij extreem hoog frequent (10MHz) dus een gewone opamp gaat niet meer werken. Ik zit te kijken naar een OPA603, die is nog redelijk in prijs en, heel belangrijk, verkrijgbaar bij mijn lokale hardware boer.

 

29 november 2014

GPS track opslaan

Filed under: Geen categorie — visserslatijn @ 17:28

De hardware werkte al tijden en nu ook de code werkt,  werd het tijd om een professionele opstelling te maken en er mee naar buiten te gaan. De BeagleBoneBlack en de GPS module hebben 5V nodig en flink wat mA. Twee lipo batterijen parallel en een dc-dc convertor uit China regelen dat deel.

Nu nog een stabiele behuizing……. ok, elastiekjes werken ook.

IMG_4720

De laatste code:


#!/usr/bin/python3

import serial
import xml.etree.ElementTree as ET

# global varables
ser = 0

#init serialport
def init_serial():
	global ser
	ser = serial.Serial()
	ser.baudrate = 115200
	ser.port = '/dev/ttyUSB0'
	ser.timeout = 1
	ser.open()
	if ser.isOpen():
		print ("open: " + ser.name)

#setup
init_serial()

#main

tijd = ""
latitude = ""
longitude = ""
datum = ""
bestandsnaam = ""
gpsfix = 0
seconde = 0
opslaan = 0
nieuwefile = 1
sluitfile = 0

while 1:
	bytes = ser.readline()
	bytes = bytes.decode("utf-8")
	if '$GNGGA' in bytes:
		lijst = bytes.split(",")
		if lijst[6] == "1":		# gps fix
			gpsfix = 1
			tijd = lijst[1]
			seconde = int(tijd[4:6])
			tijd = datum + tijd[0:2] + ":" + tijd[2:4] + ":" + tijd[4:6] + "Z"

			latitude = lijst[2] + " " + lijst[3]
			latitude1 = int(latitude[0:2])
			latitude2 = latitude[2:10]
			latitude2 = float(latitude2)
			latitude2 = latitude2 / 60
			latitude = format((latitude1 + latitude2), '.6f')

			longitude = lijst[4] + " " + lijst[5]
			longitude1 = int(longitude[0:3])
			longitude2 = float(longitude[3:10]) / 60
			longitude = format((longitude1 + longitude2), '.6f')

#			print (seconde)
			if seconde == 0:
				nieuwefile = 1
			if seconde == 59:
				sluitfile = 1
			if (seconde % 2) == 0:
				opslaan = 1
		else:
			gpsfix = 0
			print ('no fix yet')

	if '$GNZDA' in bytes:
		lijst = bytes.split(",")
		datum = lijst[4] + "-" +  lijst[3] + "-" + lijst[2] + "T"

	if ((gpsfix == 1) and (opslaan == 1)):

		if sluitfile == 1:
			tree = ET.ElementTree(root)
			tree.write('/home/debian/' + tijd + '.gpx')
			sluitfile = 0

		if nieuwefile == 1:
			root = ET.Element('gpx')
			trk = ET.SubElement(root, 'trk')
			trkseg = ET.SubElement(trk, 'trkseg')
			nieuwefile =0

		trkpt = ET.SubElement(trkseg, 'trkpt')
		trkpt.set('lat', latitude)
		trkpt.set('lon', longitude)
		trkpt.text = ''
		time = ET.SubElement(trkpt, 'time')
		time.text = tijd
		opslaan = 0

#end

Dit stukje uit de code is alles dat nodig is voor het maken van de xml file (.gpx)

elementtree2-xml

En dan natuurlijk het eveneens professionele resultaat:

elementtree-track

Er worden steeds stukjes (gpx files) met één minuut track opgeslagen, die heb ik met de hand aan elkaar geplakt en het zou best kunnen dat het deel met de elkaar kruisende lijnen verkeerd om in elkaar is gezet. Maar verder is het exact zoals het uit de GPX logger is gekomen. Nooit geweten dat ik door huizen heen kon lopen….

EDIT: Het blijkt dat de HDMI interface van de BeagleBoneBlack flink kan storen in het frequentie gebied van GPS apparatuur. Eens zien of ik dat kan uitschakelen.

26 november 2014

ElementTree, het is nog veel whoo whoo.

Filed under: Geen categorie — visserslatijn @ 22:15

Met ElementTree moet het maken van een xml simpel zijn, en waarschijnlijk is het dat ook.   Voor een gevorderde.

Mijn test programma:


import xml.etree.ElementTree as ET

root = ET.Element('gpx')
trk = ET.SubElement(root, 'trk')
trkseg = ET.SubElement(trk, 'trkseg')

trkpt = ET.SubElement(trkseg, 'trkpt')
trkpt.set('lat', '1234567890')
trkpt.set('lon', '0987654321')
trkpt.text = ''

time = ET.SubElement(trkpt, 'time')
time.text = '12:00'

trkpt = ET.SubElement(trkseg, 'trkpt')
trkpt.set('lat', 'abcdefghij')
trkpt.set('lon', 'jihgfedcba')
trkpt.text = ''

time = ET.SubElement(trkpt, 'time')
time.text = '13:00'

tree = ET.ElementTree(root)
print (ET.tostring(root))
tree.write('/tmp/output.xml')

Het resultaat:

elementtree-xml

23 november 2014

Bitbanging een XML document

Filed under: Geen categorie — visserslatijn @ 21:18

Zoals een GPIO-port door bitbanging kan veranderen in een seriele port, zo heb ik een xml document in elkaar geknutseld door strings achter elkaar te plakken en in een bestand op te slaan. Het werkt, maar fraai is het niet.

bitbang-xml

ElementTree is een betere oplossing denk ik, maar ik snap het (nog) niet.

Het resultaat:

gpx-manual

20 november 2014

Beter voorbeeld

Filed under: Geen categorie — visserslatijn @ 21:04

Dit is wat Google Earth minimaal nodig heeft om nog een track weer te geven.

gpx-minimaal

18 november 2014

Dit moet veranderen in dat.

Filed under: Geen categorie — visserslatijn @ 23:06

Het loggen werkt betrouwbaar en op de achtergrond, de code:

 


import serial

# global varables
ser = 0

#init serialport
def init_serial():
	global ser
	ser = serial.Serial()
	ser.baudrate = 115200
	ser.port = '/dev/ttyUSB0'
	ser.timeout = 1
	ser.open()
	if ser.isOpen():
		print ("open: " + ser.name)

#setup
init_serial()

#main
word1 = "$GNGGA"
word2 = "$GNZDA"

tijd = ""
latitude = ""
longitude = ""
datum = ""

gpsfix = 0
seconde = 0
opslaan = 0

while 1:
	bytes = ser.readline()
	bytes = bytes.decode("utf-8")
	if word1 in bytes:
		lijst = bytes.split(",")
		if lijst[6] == "1":		# gps fix
			gpsfix = 1
			tijd = lijst[1]
			latitude = lijst[2] + " " + lijst[3]
			longitude = lijst[4] + " " + lijst[5]
			satelliet = lijst[7]
			
			seconde = int(tijd[4:6])
			if seconde == 0:
				opslaan = 1

			tijd = tijd[0:2] + ":" + tijd[2:4] + ":" + tijd[4:6]
			latitude = latitude[0:2] + chr(176) + latitude[2:]
			longitude = longitude[0:3] + chr(176) + longitude[3:]
		else:
			gpsfix = 0

	if gpsfix == 1:
		if word2 in bytes:
			lijst = bytes.split(",")
			datum = lijst[4] + "-" +  lijst[3] + "-" + lijst[2]

	if gpsfix == 1 and opslaan == 1:
		opslaan = 0
		with open ("/media/sdcard/output.txt", "a", encoding='utf8') as bestand:
			bestand.write(datum)
			bestand.write(", ")
			bestand.write(tijd)
			bestand.write(", ")
			bestand.write(latitude)
			bestand.write(", ")
			bestand.write(longitude)
			bestand.write("\n") 
#end

De output ziet er zo uit:

2014-11-18, 19:34:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:35:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:36:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:37:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:38:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:39:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:40:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:41:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:42:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:43:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:44:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:45:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:46:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:47:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:48:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:49:00, 52°13.1973 N, 004°30.3781 E
2014-11-18, 19:50:00, 52°13.1973 N, 004°30.3781 E

Maar ik wil er iets zoals dit van maken:

gpxcode

(het lukte niet om het als tekst hier in te plakken)

16 november 2014

Python script dat NMEA data verwerkt

Filed under: Geen categorie — visserslatijn @ 09:58

Python3 op een Beaglebone Black met GPS module

bbb+gps

Dit is de data die elke seconde uit de GPS module komt:

$GNGGA,210535.000,5213.1998,N,00430.3858,E,1,12,0.9,-1.9,M,44.5,M,,0000*65
$GNGLL,5213.1998,N,00430.3858,E,210535.000,A,A*4A
$GNGSA,A,3,21,13,26,30,05,28,15,,,,,,1.7,0.9,1.4*2B
$GNGSA,A,3,77,69,67,85,68,,,,,,,,1.7,0.9,1.4*2B
$GNRMC,210535.000,A,5213.1998,N,00430.3858,E,000.0,000.0,151114,,,A*7C
$GNVTG,000.0,T,,M,000.0,N,000.0,K,A*13
$GNZDA,210535.000,15,11,2014,00,00*4B
$GNGGA,210536.000,5213.1998,N,00430.3858,E,1,12,0.9,-1.9,M,44.5,M,,0000*66
$GNGLL,5213.1998,N,00430.3858,E,210536.000,A,A*49
$GNGSA,A,3,21,13,26,30,05,28,15,,,,,,1.7,0.9,1.4*2B
$GNGSA,A,3,77,69,67,85,68,,,,,,,,1.7,0.9,1.4*2B
$GNRMC,210536.000,A,5213.1998,N,00430.3858,E,000.0,000.0,151114,,,A*7F
$GNVTG,000.0,T,,M,000.0,N,000.0,K,A*13
$GNZDA,210536.000,15,11,2014,00,00*48

Daarvan ben ik alleen (nog) geinteresseerd in de regel die start met $GNGGA omdat daar voor mij de meest interessante data zit. Tijd, latitude en longitude, GPS-lock, aantal zichtbare satellieten.


import serial

# global varables
ser = 0

#init serialport
def init_serial():
	global ser
	ser = serial.Serial()
	ser.baudrate = 115200
	ser.port = '/dev/ttyUSB0'
	ser.timeout = 1
	ser.open()
	#print if port is open or closed
	if ser.isOpen():
		print ("open: " + ser.name)

#setup
init_serial()

#main
word = "$GNGGA"
while 1:
	bytes = ser.readline()
	bytes = bytes.decode("utf-8")
#	print (len (bytes))
	if word in bytes:
#		print (bytes)
		lijst = bytes.split(",")
		if lijst[6] == "1":		# gps fix
#			print (lijst)
			tijd = lijst[1]
			latitude = lijst[2] + " " + lijst[3]
			longitude = lijst[4] + " " + lijst[5]
			satelliet = lijst[7]

			print ("time =      ", tijd)
			print ("latitude =  ", latitude)
			print ("longitude = ", longitude)
			print ("satelliet = ", satelliet)
#end

debian-bbb-gps-01

Maak een gratis website of blog op WordPress.com.