【得捷电子Follow me第1期】+4.实现定位功能
[复制链接]
本帖最后由 qinyunti 于 2023-4-9 21:17 编辑
转自公众号,欢迎关注
Raspberry Pi Pico w GPS数据采集 (qq.com)
视频
Raspberry Pi Pico w GPS数据采集 (qq.com)
一.准备
1.1GPS资料
1.2串口驱动资料
raspberry-pi-pico-python-sdk.pdf得P46Using UART on the Raspberry Pi Pico
二.接线
接到UART0 即GP0和GP1处
从以下地址 可知
串口波特率为9600
三.GPS数据接收测试
测试代码如下gps1.py
- from machine import UART, Pin
- import time
-
- uart0 = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))
-
- time.sleep(0.1)
- rxData = bytes()
- while True:
- while uart0.any() > 0:
- rxData += uart0.read(1)
- print(rxData.decode('utf-8'))
-
-
可看到打印信息如下
四.GPS数据解析
根据以上接收到得数据可以人工解析,也可以使用现成得库进行解析
将上述文档复制到开发板中
- """
- # MicropyGPS - a GPS NMEA sentence parser for Micropython/Python 3.X
- # Copyright (c) 2017 Michael Calvin McCoy (calvin.mccoy@protonmail.com)
- # The MIT License (MIT) - see LICENSE file
- """
-
-
-
-
-
-
-
- from math import floor, modf
-
-
- try:
-
- import utime
- except ImportError:
-
-
- import time
-
-
- class MicropyGPS(object):
- """GPS NMEA Sentence Parser. Creates object that stores all relevant GPS data and statistics.
- Parses sentences one character at a time using update(). """
-
-
- SENTENCE_LIMIT = 90
- __HEMISPHERES = ('N', 'S', 'E', 'W')
- __NO_FIX = 1
- __FIX_2D = 2
- __FIX_3D = 3
- __DIRECTIONS = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W',
- 'WNW', 'NW', 'NNW')
- __MONTHS = ('January', 'February', 'March', 'April', 'May',
- 'June', 'July', 'August', 'September', 'October',
- 'November', 'December')
-
- def __init__(self, local_offset=0, location_formatting='ddm'):
- """
- Setup GPS Object Status Flags, Internal Data Registers, etc
- local_offset (int): Timzone Difference to UTC
- location_formatting (str): Style For Presenting Longitude/Latitude:
- Decimal Degree Minute (ddm) - 40° 26.767′ N
- Degrees Minutes Seconds (dms) - 40° 26′ 46″ N
- Decimal Degrees (dd) - 40.446° N
- """
-
-
-
- self.sentence_active = False
- self.active_segment = 0
- self.process_crc = False
- self.gps_segments = []
- self.crc_xor = 0
- self.char_count = 0
- self.fix_time = 0
-
-
-
- self.crc_fails = 0
- self.clean_sentences = 0
- self.parsed_sentences = 0
-
-
-
- self.log_handle = None
- self.log_en = False
-
-
-
-
- self.timestamp = [0, 0, 0.0]
- self.date = [0, 0, 0]
- self.local_offset = local_offset
-
-
- self._latitude = [0, 0.0, 'N']
- self._longitude = [0, 0.0, 'W']
- self.coord_format = location_formatting
- self.speed = [0.0, 0.0, 0.0]
- self.course = 0.0
- self.altitude = 0.0
- self.geoid_height = 0.0
-
-
- self.satellites_in_view = 0
- self.satellites_in_use = 0
- self.satellites_used = []
- self.last_sv_sentence = 0
- self.total_sv_sentences = 0
- self.satellite_data = dict()
- self.hdop = 0.0
- self.pdop = 0.0
- self.vdop = 0.0
- self.valid = False
- self.fix_stat = 0
- self.fix_type = 1
-
-
-
-
- @property
- def latitude(self):
- """Format Latitude Data Correctly"""
- if self.coord_format == 'dd':
- decimal_degrees = self._latitude[0] + (self._latitude[1] / 60)
- return [decimal_degrees, self._latitude[2]]
- elif self.coord_format == 'dms':
- minute_parts = modf(self._latitude[1])
- seconds = round(minute_parts[0] * 60)
- return [self._latitude[0], int(minute_parts[1]), seconds, self._latitude[2]]
- else:
- return self._latitude
-
- @property
- def longitude(self):
- """Format Longitude Data Correctly"""
- if self.coord_format == 'dd':
- decimal_degrees = self._longitude[0] + (self._longitude[1] / 60)
- return [decimal_degrees, self._longitude[2]]
- elif self.coord_format == 'dms':
- minute_parts = modf(self._longitude[1])
- seconds = round(minute_parts[0] * 60)
- return [self._longitude[0], int(minute_parts[1]), seconds, self._longitude[2]]
- else:
- return self._longitude
-
-
-
-
- def start_logging(self, target_file, mode="append"):
- """
- Create GPS data log object
- """
-
- mode_code = 'w' if mode == 'new' else 'a'
-
- try:
- self.log_handle = open(target_file, mode_code)
- except AttributeError:
- print("Invalid FileName")
- return False
-
- self.log_en = True
- return True
-
- def stop_logging(self):
- """
- Closes the log file handler and disables further logging
- """
- try:
- self.log_handle.close()
- except AttributeError:
- print("Invalid Handle")
- return False
-
- self.log_en = False
- return True
-
- def write_log(self, log_string):
- """Attempts to write the last valid NMEA sentence character to the active file handler
- """
- try:
- self.log_handle.write(log_string)
- except TypeError:
- return False
- return True
-
-
-
-
- def gprmc(self):
- """Parse Recommended Minimum Specific GPS/Transit data (RMC)Sentence.
- Updates UTC timestamp, latitude, longitude, Course, Speed, Date, and fix status
- """
-
-
- try:
- utc_string = self.gps_segments[1]
-
- if utc_string:
- hours = (int(utc_string[0:2]) + self.local_offset) % 24
- minutes = int(utc_string[2:4])
- seconds = float(utc_string[4:])
- self.timestamp = [hours, minutes, seconds]
- else:
- self.timestamp = [0, 0, 0.0]
-
- except ValueError:
- return False
-
-
- try:
- date_string = self.gps_segments[9]
-
-
-
- if date_string:
- day = int(date_string[0:2])
- month = int(date_string[2:4])
- year = int(date_string[4:6])
- self.date = (day, month, year)
- else:
- self.date = (0, 0, 0)
-
- except ValueError:
- return False
-
-
- if self.gps_segments[2] == 'A':
-
-
- try:
-
- l_string = self.gps_segments[3]
- lat_degs = int(l_string[0:2])
- lat_mins = float(l_string[2:])
- lat_hemi = self.gps_segments[4]
-
-
- l_string = self.gps_segments[5]
- lon_degs = int(l_string[0:3])
- lon_mins = float(l_string[3:])
- lon_hemi = self.gps_segments[6]
- except ValueError:
- return False
-
- if lat_hemi not in self.__HEMISPHERES:
- return False
-
- if lon_hemi not in self.__HEMISPHERES:
- return False
-
-
- try:
- spd_knt = float(self.gps_segments[7])
- except ValueError:
- return False
-
-
- try:
- if self.gps_segments[8]:
- course = float(self.gps_segments[8])
- else:
- course = 0.0
- except ValueError:
- return False
-
-
-
-
- self._latitude = [lat_degs, lat_mins, lat_hemi]
- self._longitude = [lon_degs, lon_mins, lon_hemi]
-
- self.speed = [spd_knt, spd_knt * 1.151, spd_knt * 1.852]
- self.course = course
- self.valid = True
-
-
- self.new_fix_time()
-
- else:
- self._latitude = [0, 0.0, 'N']
- self._longitude = [0, 0.0, 'W']
- self.speed = [0.0, 0.0, 0.0]
- self.course = 0.0
- self.valid = False
-
- return True
-
- def gpgll(self):
- """Parse Geographic Latitude and Longitude (GLL)Sentence. Updates UTC timestamp, latitude,
- longitude, and fix status"""
-
-
- try:
- utc_string = self.gps_segments[5]
-
- if utc_string:
- hours = (int(utc_string[0:2]) + self.local_offset) % 24
- minutes = int(utc_string[2:4])
- seconds = float(utc_string[4:])
- self.timestamp = [hours, minutes, seconds]
- else:
- self.timestamp = [0, 0, 0.0]
-
- except ValueError:
- return False
-
-
- if self.gps_segments[6] == 'A':
-
-
- try:
-
- l_string = self.gps_segments[1]
- lat_degs = int(l_string[0:2])
- lat_mins = float(l_string[2:])
- lat_hemi = self.gps_segments[2]
-
-
- l_string = self.gps_segments[3]
- lon_degs = int(l_string[0:3])
- lon_mins = float(l_string[3:])
- lon_hemi = self.gps_segments[4]
- except ValueError:
- return False
-
- if lat_hemi not in self.__HEMISPHERES:
- return False
-
- if lon_hemi not in self.__HEMISPHERES:
- return False
-
-
- self._latitude = [lat_degs, lat_mins, lat_hemi]
- self._longitude = [lon_degs, lon_mins, lon_hemi]
- self.valid = True
-
-
- self.new_fix_time()
-
- else:
- self._latitude = [0, 0.0, 'N']
- self._longitude = [0, 0.0, 'W']
- self.valid = False
-
- return True
-
- def gpvtg(self):
- """Parse Track Made Good and Ground Speed (VTG) Sentence. Updates speed and course"""
- try:
- course = float(self.gps_segments[1]) if self.gps_segments[1] else 0.0
- spd_knt = float(self.gps_segments[5]) if self.gps_segments[5] else 0.0
- except ValueError:
- return False
-
-
- self.speed = (spd_knt, spd_knt * 1.151, spd_knt * 1.852)
- self.course = course
- return True
-
- def gpgga(self):
- """Parse Global Positioning System Fix Data (GGA) Sentence. Updates UTC timestamp, latitude, longitude,
- fix status, satellites in use, Horizontal Dilution of Precision (HDOP), altitude, geoid height and fix status"""
-
- try:
-
- utc_string = self.gps_segments[1]
-
-
- if utc_string:
- hours = (int(utc_string[0:2]) + self.local_offset) % 24
- minutes = int(utc_string[2:4])
- seconds = float(utc_string[4:])
- else:
- hours = 0
- minutes = 0
- seconds = 0.0
-
-
- satellites_in_use = int(self.gps_segments[7])
-
-
- fix_stat = int(self.gps_segments[6])
-
- except (ValueError, IndexError):
- return False
-
- try:
-
- hdop = float(self.gps_segments[8])
- except (ValueError, IndexError):
- hdop = 0.0
-
-
- if fix_stat:
-
-
- try:
-
- l_string = self.gps_segments[2]
- lat_degs = int(l_string[0:2])
- lat_mins = float(l_string[2:])
- lat_hemi = self.gps_segments[3]
-
-
- l_string = self.gps_segments[4]
- lon_degs = int(l_string[0:3])
- lon_mins = float(l_string[3:])
- lon_hemi = self.gps_segments[5]
- except ValueError:
- return False
-
- if lat_hemi not in self.__HEMISPHERES:
- return False
-
- if lon_hemi not in self.__HEMISPHERES:
- return False
-
-
- try:
- altitude = float(self.gps_segments[9])
- geoid_height = float(self.gps_segments[11])
- except ValueError:
- altitude = 0
- geoid_height = 0
-
-
- self._latitude = [lat_degs, lat_mins, lat_hemi]
- self._longitude = [lon_degs, lon_mins, lon_hemi]
- self.altitude = altitude
- self.geoid_height = geoid_height
-
-
- self.timestamp = [hours, minutes, seconds]
- self.satellites_in_use = satellites_in_use
- self.hdop = hdop
- self.fix_stat = fix_stat
-
-
- if fix_stat:
- self.new_fix_time()
-
- return True
-
- def gpgsa(self):
- """Parse GNSS DOP and Active Satellites (GSA) sentence. Updates GPS fix type, list of satellites used in
- fix calculation, Position Dilution of Precision (PDOP), Horizontal Dilution of Precision (HDOP), Vertical
- Dilution of Precision, and fix status"""
-
-
- try:
- fix_type = int(self.gps_segments[2])
- except ValueError:
- return False
-
-
- sats_used = []
- for sats in range(12):
- sat_number_str = self.gps_segments[3 + sats]
- if sat_number_str:
- try:
- sat_number = int(sat_number_str)
- sats_used.append(sat_number)
- except ValueError:
- return False
- else:
- break
-
-
- try:
- pdop = float(self.gps_segments[15])
- hdop = float(self.gps_segments[16])
- vdop = float(self.gps_segments[17])
- except ValueError:
- return False
-
-
- self.fix_type = fix_type
-
-
- if fix_type > self.__NO_FIX:
- self.new_fix_time()
-
- self.satellites_used = sats_used
- self.hdop = hdop
- self.vdop = vdop
- self.pdop = pdop
-
- return True
-
- def gpgsv(self):
- """Parse Satellites in View (GSV) sentence. Updates number of SV Sentences,the number of the last SV sentence
- parsed, and data on each satellite present in the sentence"""
- try:
- num_sv_sentences = int(self.gps_segments[1])
- current_sv_sentence = int(self.gps_segments[2])
- sats_in_view = int(self.gps_segments[3])
- except ValueError:
- return False
-
-
-
- satellite_dict = dict()
-
-
- if num_sv_sentences == current_sv_sentence:
-
- sat_segment_limit = (sats_in_view - ((num_sv_sentences - 1) * 4)) * 5
- else:
- sat_segment_limit = 20
-
-
- for sats in range(4, sat_segment_limit, 4):
-
-
- if self.gps_segments[sats]:
- try:
- sat_id = int(self.gps_segments[sats])
- except (ValueError,IndexError):
- return False
-
- try:
- elevation = int(self.gps_segments[sats+1])
- except (ValueError,IndexError):
- elevation = None
-
- try:
- azimuth = int(self.gps_segments[sats+2])
- except (ValueError,IndexError):
- azimuth = None
-
- try:
- snr = int(self.gps_segments[sats+3])
- except (ValueError,IndexError):
- snr = None
-
- else:
- break
-
-
- satellite_dict[sat_id] = (elevation, azimuth, snr)
-
-
- self.total_sv_sentences = num_sv_sentences
- self.last_sv_sentence = current_sv_sentence
- self.satellites_in_view = sats_in_view
-
-
-
- if current_sv_sentence == 1:
- self.satellite_data = satellite_dict
- else:
- self.satellite_data.update(satellite_dict)
-
- return True
-
-
-
-
-
- def new_sentence(self):
- """Adjust Object Flags in Preparation for a New Sentence"""
- self.gps_segments = ['']
- self.active_segment = 0
- self.crc_xor = 0
- self.sentence_active = True
- self.process_crc = True
- self.char_count = 0
-
- def update(self, new_char):
- """Process a new input char and updates GPS object if necessary based on special characters ('$', ',', '*')
- Function builds a list of received string that are validate by CRC prior to parsing by the appropriate
- sentence function. Returns sentence type on successful parse, None otherwise"""
-
- valid_sentence = False
-
-
- ascii_char = ord(new_char)
-
- if 10 <= ascii_char <= 126:
- self.char_count += 1
-
-
- if self.log_en:
- self.write_log(new_char)
-
-
- if new_char == '$':
- self.new_sentence()
- return None
-
- elif self.sentence_active:
-
-
- if new_char == '*':
- self.process_crc = False
- self.active_segment += 1
- self.gps_segments.append('')
- return None
-
-
-
- elif new_char == ',':
- self.active_segment += 1
- self.gps_segments.append('')
-
-
- else:
- self.gps_segments[self.active_segment] += new_char
-
-
- if not self.process_crc:
-
- if len(self.gps_segments[self.active_segment]) == 2:
- try:
- final_crc = int(self.gps_segments[self.active_segment], 16)
- if self.crc_xor == final_crc:
- valid_sentence = True
- else:
- self.crc_fails += 1
- except ValueError:
- pass
-
-
- if self.process_crc:
- self.crc_xor ^= ascii_char
-
-
- if valid_sentence:
- self.clean_sentences += 1
- self.sentence_active = False
-
- if self.gps_segments[0] in self.supported_sentences:
-
-
- if self.supported_sentences[self.gps_segments[0]](self):
-
-
- self.parsed_sentences += 1
- return self.gps_segments[0]
-
-
- if self.char_count > self.SENTENCE_LIMIT:
- self.sentence_active = False
-
-
- return None
-
- def new_fix_time(self):
- """Updates a high resolution counter with current time when fix is updated. Currently only triggered from
- GGA, GSA and RMC sentences"""
- try:
- self.fix_time = utime.ticks_ms()
- except NameError:
- self.fix_time = time.time()
-
-
-
-
-
-
- def satellite_data_updated(self):
- """
- Checks if the all the GSV sentences in a group have been read, making satellite data complete
- :return: boolean
- """
- if self.total_sv_sentences > 0 and self.total_sv_sentences == self.last_sv_sentence:
- return True
- else:
- return False
-
- def unset_satellite_data_updated(self):
- """
- Mark GSV sentences as read indicating the data has been used and future updates are fresh
- """
- self.last_sv_sentence = 0
-
- def satellites_visible(self):
- """
- Returns a list of of the satellite PRNs currently visible to the receiver
- :return: list
- """
- return list(self.satellite_data.keys())
-
- def time_since_fix(self):
- """Returns number of millisecond since the last sentence with a valid fix was parsed. Returns 0 if
- no fix has been found"""
-
-
- if self.fix_time == 0:
- return -1
-
-
-
- try:
- current = utime.ticks_diff(utime.ticks_ms(), self.fix_time)
- except NameError:
- current = (time.time() - self.fix_time) * 1000
-
- return current
-
- def compass_direction(self):
- """
- Determine a cardinal or inter-cardinal direction based on current course.
- :return: string
- """
-
- if self.course >= 348.75:
- offset_course = 360 - self.course
- else:
- offset_course = self.course + 11.25
-
-
- dir_index = floor(offset_course / 22.5)
-
- final_dir = self.__DIRECTIONS[dir_index]
-
- return final_dir
-
- def latitude_string(self):
- """
- Create a readable string of the current latitude data
- :return: string
- """
- if self.coord_format == 'dd':
- formatted_latitude = self.latitude
- lat_string = str(formatted_latitude[0]) + '° ' + str(self._latitude[2])
- elif self.coord_format == 'dms':
- formatted_latitude = self.latitude
- lat_string = str(formatted_latitude[0]) + '° ' + str(formatted_latitude[1]) + "' " + str(formatted_latitude[2]) + '" ' + str(formatted_latitude[3])
- else:
- lat_string = str(self._latitude[0]) + '° ' + str(self._latitude[1]) + "' " + str(self._latitude[2])
- return lat_string
-
- def longitude_string(self):
- """
- Create a readable string of the current longitude data
- :return: string
- """
- if self.coord_format == 'dd':
- formatted_longitude = self.longitude
- lon_string = str(formatted_longitude[0]) + '° ' + str(self._longitude[2])
- elif self.coord_format == 'dms':
- formatted_longitude = self.longitude
- lon_string = str(formatted_longitude[0]) + '° ' + str(formatted_longitude[1]) + "' " + str(formatted_longitude[2]) + '" ' + str(formatted_longitude[3])
- else:
- lon_string = str(self._longitude[0]) + '° ' + str(self._longitude[1]) + "' " + str(self._longitude[2])
- return lon_string
-
- def speed_string(self, unit='kph'):
- """
- Creates a readable string of the current speed data in one of three units
- :param unit: string of 'kph','mph, or 'knot'
- :return:
- """
- if unit == 'mph':
- speed_string = str(self.speed[1]) + ' mph'
-
- elif unit == 'knot':
- if self.speed[0] == 1:
- unit_str = ' knot'
- else:
- unit_str = ' knots'
- speed_string = str(self.speed[0]) + unit_str
-
- else:
- speed_string = str(self.speed[2]) + ' km/h'
-
- return speed_string
-
- def date_string(self, formatting='s_mdy', century='20'):
- """
- Creates a readable string of the current date.
- Can select between long format: Januray 1st, 2014
- or two short formats:
- 11/01/2014 (MM/DD/YYYY)
- 01/11/2014 (DD/MM/YYYY)
- :param formatting: string 's_mdy', 's_dmy', or 'long'
- :param century: int delineating the century the GPS data is from (19 for 19XX, 20 for 20XX)
- :return: date_string string with long or short format date
- """
-
-
- if formatting == 'long':
-
- month = self.__MONTHS[self.date[1] - 1]
-
-
- if self.date[0] in (1, 21, 31):
- suffix = 'st'
- elif self.date[0] in (2, 22):
- suffix = 'nd'
- elif self.date[0] == (3, 23):
- suffix = 'rd'
- else:
- suffix = 'th'
-
- day = str(self.date[0]) + suffix
-
- year = century + str(self.date[2])
-
- date_string = month + ' ' + day + ', ' + year
-
- else:
-
- if self.date[0] < 10:
- day = '0' + str(self.date[0])
- else:
- day = str(self.date[0])
-
-
- if self.date[1] < 10:
- month = '0' + str(self.date[1])
- else:
- month = str(self.date[1])
-
-
- if self.date[2] < 10:
- year = '0' + str(self.date[2])
- else:
- year = str(self.date[2])
-
-
- if formatting == 's_dmy':
- date_string = day + '/' + month + '/' + year
-
- else:
- date_string = month + '/' + day + '/' + year
-
- return date_string
-
-
- supported_sentences = {'GPRMC': gprmc, 'GLRMC': gprmc,
- 'GPGGA': gpgga, 'GLGGA': gpgga,
- 'GPVTG': gpvtg, 'GLVTG': gpvtg,
- 'GPGSA': gpgsa, 'GLGSA': gpgsa,
- 'GPGSV': gpgsv, 'GLGSV': gpgsv,
- 'GPGLL': gpgll, 'GLGLL': gpgll,
- 'GNGGA': gpgga, 'GNRMC': gprmc,
- 'GNVTG': gpvtg, 'GNGLL': gpgll,
- 'GNGSA': gpgsa,
- }
-
- if __name__ == "__main__":
- pass
-
新建测试文件gps2.py
- from machine import UART, Pin
- import time
- import hashlib
- from micropyGPS import MicropyGPS
-
- from machine import Pin, I2C
- from ssd1306 import SSD1306_I2C
- import framebuf
-
- WIDTH = 128
- HEIGHT = 32
-
- i2c = I2C(0)
- oled = SSD1306_I2C(WIDTH, HEIGHT, i2c)
-
- uart0 = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))
-
- time.sleep(0.1)
- rxData = bytes()
- my_gps = MicropyGPS()
- while True:
- if uart0.any():
- stat = my_gps.update(uart0.read(1).decode('ascii'))
- if stat:
- print('Latitude:', my_gps.latitude_string())
- print('Longitude:', my_gps.longitude_string())
- print('Speed:', my_gps.speed_string('kph'), 'or', my_gps.speed_string('mph'), 'or', my_gps.speed_string('knot'))
- print('Date (Long Format):', my_gps.date_string('long'))
- print('Date (Short D/M/Y Format):', my_gps.date_string('s_dmy'))
- print('timestamp (Short [H,M,S] Format):', my_gps.timestamp)
- oled.text('Latitude:',0,0)
- oled.text(my_gps.latitude_string(),0,7)
- oled.text('Longitude:',0,14)
- oled.text(my_gps.longitude_string(),0,21)
- oled.show()
- stat = None
-
-
打印如下 经纬度,速度,日期,时间
- Latitude: 28° 16.08602' N
-
- Longitude: 112° 52.46381' E
-
- Speed: 0.0 km/h or 0.0 mph or 0.0 knots
-
- Date (Long Format): April 8th, 2023
-
- Date (Short D/M/Y Format): 08/04/23
-
- timestamp (Short [H,M,S] Format): [14, 40, 34.0]
-
- Latitude: 28° 16.08602' N
-
- Longitude: 112° 52.46381' E
-
- Speed: 0.0 km/h or 0.0 mph or 0.0 knots
-
- Date (Long Format): April 8th, 2023
-
- Date (Short D/M/Y Format): 08/04/23
-
- timestamp (Short [H,M,S] Format): [14, 40, 34.0]
-
- Latitude: 28° 16.08602' N
-
- Longitude: 112° 52.46381' E
-
- Speed: 0.0 km/h or 0.0 mph or 0.0 knots
-
- Date (Long Format): April 8th, 2023
-
- Date (Short D/M/Y Format): 08/04/23
-
- timestamp (Short [H,M,S] Formattime. Sleep, 34.0]
oled显示
|