GPS module neo-6m

도구의인간 메이커스 위키
이동: 둘러보기, 검색

GPS + raspberry pi

gps module

Neo-6m gps 모듈을 사용한다. 대부분의 gps 모듈이 serial UART 통신을 사용한다. 참고자료가 많은 쪽이 좋으므로 시리얼 통신방식 부품을 고른다.
||600

 5Hz position update rate  EEPROM to save configuration settings  Rechargeable battery for Backup  The cold start time of 38 s and Hot start time of 1 s  Configurable from 4800 Baud to 115200 Baud rates. (default 9600)  SuperSense ® Indoor GPS: -162 dBm tracking sensitivity  Support SBAS (WAAS, EGNOS, MSAS, GAGAN)  Separated 18X18mm GPS antenna  Supply voltage: 3.3 V

회로 구성

라즈베리의 8번째, 10번째 핀이 시리얼 통신용이다.
||600


||600

라즈베리파이 시리얼 포트 설정

참고: http://lhdangerous.godohosting.com/wiki/index.php/%EB%9D%BC%EC%A6%88%EB%B2%A0%EB%A6%AC%ED%8C%8C%EC%9D%B4_%EC%8B%9C%EB%A6%AC%EC%96%BC_%ED%86%B5%EC%8B%A0_%EC%84%A4%EC%A0%95_(setting_up_Raspberry_pi_serial_connection_via_GPIO)

GPS FIX

  • gps에서 사용되는 중요한 개념중에 FIX라는 것이 있다. 디바이스를 부팅하고나서도 위성으로부터 전파를 받아 디바이스의 위치를 계산할 수 았으려면 조금 시간이 걸리는데, 이렇게 충분한 데이터를 모아 자신의 위치를 계산할 수 있게되면 fix되었다라고 이야기한다. 더 구체적인 내용은 공부를 더 해보자.

  • neo-6m gps는 유난히 fix 하는데 시간이 오래 걸리는 듯 하다. 위성 신호가 잘 잡혀도 길게는 10분정도 걸리는 경우가 있다.

NMEA 프로토콜

gps 통신 표준을 관리하는 곳은 재미있게도 NMEA, 'National Marine Electronics Association' 해양전자협회? 이다. 그래서 gps표준 프로토콜 이름도 'NMEA-0183' 이다.
잘 정리된 한글자료: https://techlog.gurucat.net/239 [하얀쿠아의 이것저것 만들기 Blog] nmea 표준 레퍼런스: https://www.sparkfun.com/datasheets/GPS/NMEA%20Reference%20Manual-Rev2.1-Dec07.pdf

NMEA 데이터 형태


$GPGSA,A,3,02,07,01,20,04,13,,,,,,,3.7,1.7,3.2*31

$GPRMC,141113.999,A,3730.0308,N,12655.2369,E,19.77,195.23,101200,,*3C

$GPGGA,141114.999,3730.0264,N,12655.2351,E,1,07,1.2,98.8,M,,,,0000*3C

$GPGSA,A,3,02,07,01,20,24,04,13,,,,,,2.3,1.2,1.9*3E

$GPRMC,141114.999,A,3730.0264,N,12655.2351,E,15.51,202.12,101200,,*3C

$GPGGA,141115.999,3730.0231,N,12655.2345,E,1,07,1.2,98.7,M,,,,0000*37

$GPGSA,A,3,02,07,01,20,24,04,13,,,,,,2.3,1.2,1.9*3E

$GPGSV,2,1,07,07,84,025,47,04,51,289,48,20,40,048,47,02,32,203,46*74

$GPGSV,2,2,07,01,23,101,47,13,20,131,32,24,19,268,40*49

$GPRMC,141115.999,A,3730.0231,N,12655.2345,E,12.14,194.75,101200,,*33

$GPGGA,141116.999,3730.0210,N,12655.2330,E,1,07,1.2,98.5,M,,,,0000*37

$GPGSA,A,3,02,07,01,20,24,04,13,,,,,,2.3,1.2,1.9*3E

$GPRMC,141116.999,A,3730.0210,N,12655.2330,E,8.01,194.65,101200,,*0F

$GPGGA,141117.998,3730.0199,N,12655.2320,E,1,06,1.3,98.2,M,,,,0000*33

$GPGSA,A,3,02,07,01,20,24,04,,,,,,,2.4,1.3,2.0*30
  • $ : 각 문장의 시작
  • \n (CR/LF) : 각 문장의 끝
  • sentence id : $다음에 오는 다섯 글자로,디바이스 종류를 밝히는두자(예를들어 GP) 와 이어지는 데이터 종류를 밝히는 3글자(예를들어 GGA)로 구성됨.
  • 데이터: 콤마로 구분된 데이터들은 앞의 sentence id에 따라 해석하게 된다. 가장 기본적인 몇가지만 읽는 방법을 알아보자

    GPGGA

    GPGGA는 'Global Positioning System Fix Data'를 의미한다. 아래의 GPGGA 예제 sentence를 살펴보자.

GPGGA는 총 17개의 field를 가진다.
||600
||600

GPRMC

GPRMC는 'Recommended Minimum Specific GNSS Data'로 정의되어있다. NMEA에는 GPS에서 필수적인 PVT(Position, Velocity, Time) 데이터의 고유한 버전이 있다. 그것을 RMC라고 부른다. 'RMC'에서 RM이 Recommended Minimum의 약자이고, C는 GNSS를 의미한다. RMA, RMB도 있으며 각각 LORAN-C와 Navigation을 의미한다. 상식으로 알아두자.
||600
||600

GPGSV

GSV는 'GNSS Satellite in View' 를 의미한다. 각각 위성의 상태에 대해 나와있는 문장이다.
||600

checksum

NMEA checksum 계산방법 및 예제코드 지금까지 예제로 보여준 NMEA sentence들을 살펴보면 알겠지만, checksum은 * 뒤에 16진수 두자리로 표시한다.

checksum의 계산은 $, * 의 사이에 있는 각 문자를 exclusive-or (XOR) 연산한 값을 표시한다.

어떤 NMEA sentence가 주어지면, * 뒤에 있는 16진수 두자리의 checksum과 직접 XOR연산한 값이 일치하는지를 검사해야 한다. 일치하지 않는다면, 그 sentence는 통신상의 문제 등의 이유로, 결함이 있는 것으로 간주해야 하며, 사용하면 안된다. 예를 들어보자.

$GPRMC,155123.000,A,4043.8432,N,07359.7653,W,0.15,83.25,200407,,*28

이와 같은 NMEA sentence가 주어졌을때, checksum계산을 위한 XOR연산은 아래 각 문자들에 대해서 수행하면 된다.

GPRMC,155123.000,A,4043.8432,N,07359.7653,W,0.15,83.25,200407,,

python 에서 NMEA 데이터 사용 예제

manually

참고: http://ozzmaker.com/using-python-with-a-gps-receiver-on-a-raspberry-pi/

import serial

port = "/dev/serial0"

def parseGPS(data):
#    print "raw:", data #prints raw data
    if data[0:6] == "$GPRMC":
        sdata = data.split(",")
        if sdata[2] == 'V':
            print "no satellite data available"
            return
        print "---Parsing GPRMC---",
        time = sdata[1][0:2] + ":" + sdata[1][2:4] + ":" + sdata[1][4:6]
        lat = decode(sdata[3]) #latitude
        dirLat = sdata[4]      #latitude direction N/S
        lon = decode(sdata[5]) #longitute
        dirLon = sdata[6]      #longitude direction E/W
        speed = sdata[7]       #Speed in knots
        trCourse = sdata[8]    #True course
        date = sdata[9][0:2] + "/" + sdata[9][2:4] + "/" + sdata[9][4:6]#date

        print "time : %s, latitude : %s(%s), longitude : %s(%s), speed : %s, True Course : %s, Date : %s" %  (time,lat,dirLat,lon,dirLon,speed,trCourse,date)

def decode(coord):
    #Converts DDDMM.MMMMM > DD deg MM.MMMMM min
    x = coord.split(".")
    head = x[0]
    tail = x[1]
    deg = head[0:-2]
    min = head[-2:]
    return deg + " deg " + min + "." + tail + " min"


print "Receiving GPS data"
ser = serial.Serial(port, baudrate = 9600, timeout = 0.5)
while True:
   data = ser.readline()
   parseGPS(data)

pynmea2 library 사용

NMEA 데이터 파싱을 도와주는 pynmea2 라이브러리를 사용할 수 있다.

  • 라이브러리 설치
    $ pip install pynmea2
  • 예제
import serial
import pynmea2

port = "/dev/serial0"

def parseGPS(str):
    if str.find('GGA') > 0:
        msg = pynmea2.parse(str)
        print "Timestamp: %s -- Lat: %s %s -- Lon: %s %s -- Altitude: %s %s -- Satellites: %s" % (msg.timestamp,msg.lat,msg.lat_dir,msg.lon,msg.lon_dir,msg.altitude,msg.altitude_units,msg.num_sats)

serialPort = serial.Serial(port, baudrate = 9600, timeout = 0.5)
while True:
    str = serialPort.readline()
    parseGPS(str)

GPSD

gpsd 는 GPS daemon의 약자로, 백그라운드에서 동작하면서 gps 사용을 도와주는 프로그램이다. 시리얼포트에서 들어오는 로데이터를 바로 사용할 수도 있지만, gpsd과, 이와 연계된 라이브러리를 사용하면 더 쉽게 gps가 제공하는 정보를 활용할 수 있다. Adafruit 참고자료: https://learn.adafruit.com/adafruit-ultimate-gps-hat-for-raspberry-pi/use-gpsd gpsd 공식문서:https://gpsd.gitlab.io/gpsd/index.html

gpsd 와 파이썬 라이브러리 설치

$ sudo apt-get update
$ sudo apt-get install gpsd gpsd-clients python-gps

라즈비안의 디폴트 서비스 제거

라즈비안에서는 라즈베리파이에 USB를 통해 외부 gps장치를 연결이 감지되면 gpsd가 실행되도록하는 systemd 서비스가 기본 켜져있다. 이 부분이 간섭일으키므로 disable 시킨다.

$ sudo systemctl stop gpsd.socket
$ sudo systemctl disable gpsd.socket

부팅시 gpsd 자동으로 시작하기

  • [ ] systemd를 죽이는 대신에 usb가 아닌 serial0에 연결된 gps 가 부팅과 동시에 실행되도록 하려면, /etc/default/gpsd 를 수정토록 한다.
    $ sudo nano /etc/default/gpsd

    from:

    DEVICES=""

to:

DEVICES="/dev/serial0"

그리고 systemd 서비스는 다시 켜준다.

 $ sudo systemctl enable gpsd.socket
 $ sudo systemctl start gpsdsocket

gpsd 실행

$ sudo gpsd /dev/serial0 -F /var/run/gpsd.sock

끌 때는

$ sudo killall gpsd

test: cgps

gpsd가 제공하는 데이터를 실시간으로 확인할 수 있다.

$ cgps


||600

gpsd 가 제공하는 데이터 구조

gpsd의 데이터는 json 형식으로, 'class' attribute 에 따라 해석한다. gpsd 프로토콜 공식문서: https://gpsd.gitlab.io/gpsd/gpsd_json.html

gpsd + python 예제

  • gpsd로부터 TPV (time, position, velocity) 기본정보 가져오기
#! /usr/bin/python

from gps import *
import time

gpsd = gps(mode=WATCH_ENABLE|WATCH_NEWSTYLE)
print 'latitude\tlongitude\ttime utc\t\t\taltitude\tepv\tept\tspeed\tclimb' # '\t' = TAB to try and output the data in columns.

try:


    while True:
        report = gpsd.next() #
        if report['class'] == 'TPV':

            print  getattr(report,'lat',0.0),"\t",
            print  getattr(report,'lon',0.0),"\t",
            print getattr(report,'time',''),"\t",
            print  getattr(report,'alt','nan'),"\t\t",
            print  getattr(report,'epv','nan'),"\t",
            print  getattr(report,'ept','nan'),"\t",
            print  getattr(report,'speed','nan'),"\t",
            print getattr(report,'climb','nan'),"\t"

        time.sleep(1)

except (KeyboardInterrupt, SystemExit): #when you press ctrl+c
    print "Done.\nExiting."
  • 인공위성 정보 가져오기
#! /usr/bin/python

from gps import *
import time
import os

gpsd = gps(mode=WATCH_ENABLE|WATCH_NEWSTYLE)

try:
    while True:

        report = gpsd.next() #
        if report['class'] == 'SKY':
            os.system('clear')
            print ' Satellites (total of', len(gpsd.satellites) , ' in view)'
            for i in gpsd.satellites:
                print 't', i


            print '\n\n'
            print 'PRN = PRN ID of the satellite. 1-63 are GNSS satellites, 64-96 are GLONASS satellites, 100-164 are SBAS satellites'
            print 'E = Elevation in degrees'
            print 'As = Azimuth, degrees from true north'
            print 'ss = Signal stength in dB'
            print 'used = Used in current solution?'

        time.sleep(1)


except (KeyboardInterrupt, SystemExit): #when you press ctrl+c
    print "Done.\nExiting."