GPS RTK devboard from Unicorecomm
tags: RTK Unicorecomm, GPS
1. Unicorecomm GPS
1.1 Ressources
- Module GPS: Unicorecomm UB4B0 (UB4B0 Brief, UB4B0 User Manual)
- Carte d'adaptation pour développement: HPL-EVK v4 (HPL-EVK v4 User Manual)
- Manuel d'utilisation des commandes: Reference Commands Manual, Preliminary Commands Reference Manual (certaines commandes ne sont pas documentées dans la version release), TCP CONFIG
- Logiciels: CDT 2.2.13, UPrecise
1.2 Utilisation
- Débranchez l'alimentation et mettez l'interrupteur d'alimentation en position éteinte (sur la gauche)
- Connectez la carte UB4B0 sur la carte de développement HPL-EVK
- Via port série:
- Si vous n'avez pas de port RS232, connectez le port RS232 COM3 à un convertisseur RS232↔USB.
La carte utilisé ici est un DFRobot Multiplexer v1.1, connecté sur le port B-232 (fil rouge PC TX → RX pin 3 RS-232, fil orange PC RX ← TX pin 2 RS-232, masse sur pin 5)
- Via port ethernet:
- La carte a comme adresse IP
192.168.0.100
. Vous pouvez connecter votre ordinateur en direct sur la carte et définir l'adresse IP de votre ordinateur sur un sous-réseau valide (ex: adresse IP de l'ordinateur:192.1680.42
et masque de sous-réseau255.255.255.0
). - Voir les commandes disponible dans le document TCP CONFIG pour changer l'adresse IP ou configurer le DHCP.
- Mettez sous tension en glissant l'interrupteur d'alimentation en position allumé (sur la droite)
Vous pouvez maintenant lancer UPrecise (plus récent) ou CDT (ne tient pas compte des satellites en L5) et configurer la connexion sur le port série de votre machine (baudrate 115200) ou sur l'adresse IP 192.168.0.100 si vous êtes en ethernet.
Vous avez aussi la possibilité d'utiliser une console série (putty
sous Windows, dterm
sous linux par exemple) ou une connexion par telnet sur le port 40000 (telnet 192.168.0.100 40000
) si vous êtes connecté par ethernet afin d'envoyer directement des commandes de configuration au module GPS.
2. Configuration en Base/Station
Depuis la console UPrecise (ou n'importe quelle moyen de communication avec le GPS), envoyez les commandes suivantes:
2.1 Position connue
Remplacez LAT
, LONG
et HEIGHT
avec les coordonnées connues de la base.
UNLOGALL COM2 FIX NONE MODE BASE LAT LONG HEIGHT LOG COM2 RTCM1006 ontime 10 LOG COM2 RTCM1033 ontime 10 LOG COM2 RTCM1074 ontime 1 LOG COM2 RTCM1084 ontime 1 LOG COM2 RTCM1094 ontime 1 LOG COM2 RTCM1124 ontime 1
2.2 Calcul automatique de la position
La ligne 3 permet de configurer les paramètres de calcul automatique de la position. Le format est le suivant:
MODE BASE TIME SECONDS
HORIZONTAL
VERTICAL
HORIZONTAL
etVERTICAL
correspondent à l'écart type autorisé en latitude/longitude et altitude en mètre;SECONDS
correspond au timeout dans le cas où la précision demandé n'est pas atteinte.
UNLOGALL COM2 FIX NONE MODE BASE TIME 60 1.5 2.5 LOG COM2 RTCM1006 ontime 10 LOG COM2 RTCM1033 ontime 10 LOG COM2 RTCM1074 ontime 1 LOG COM2 RTCM1084 ontime 1 LOG COM2 RTCM1094 ontime 1 LOG COM2 RTCM1124 ontime 1
3. Développement
3.1 Trouver le port de communication
Sous Linux et OSX, lancez la commande dmesg -w
et connectez le port USB afin de voir sur quel port la carte est attribué.
Sur Windows, le format du port est COMx
, `x` étant un nombre défini par Windows. Lancez le Gestionnaire de périphérique et branchez le port USB pour repérer le port attribué par Windows
3.2 Exemple
Voici un exemple de code Python utilisant la bibliothèque pyserial pour configurer la carte pour qu'elle transmette sa meilleure position connue toutes les secondes:
#!/usr/bin/python3 # pip install typing pyserial from typing import NamedTuple, Union import threading import serial import re import datetime from time import time, sleep BestPosition = NamedTuple('BestPosition', [ ('gps_week', int), ('gps_sec', float), ('solution_type', str), ('position_type', str), ('gps_latitude', str), ('gps_longitude', str), ('gps_altitude', str), ('gps_date', str) ]) class UB4B0_rover(): def __init__(self, port, baudrate=115200, timeout=3): self.serial = serial.Serial(port=port, baudrate=baudrate, timeout=timeout) self.lock = threading.Lock() # Send rover configuration self.send_command('UNLOGALL', waitResp=False) # Remove previous logs self.send_command('FIX none', waitResp=False) # Remove any previousl fixed position (base station) self.send_command('SAVECONFIG', waitResp=False) # Save configuration def close(self): self.serial.close() def write(self, data: bytes): with self.lock: self.serial.write(data) def send_command(self, command: str, waitResp: bool = True) -> str: with self.lock: # Flush input buffer self.serial.reset_input_buffer() # Send command self.serial.write(bytes(command + '\r\n', 'utf-8')) # Expected response should be `$command,{command},response: OK*{checksum}\r\n` # There is no info how the checksum is calculated, so skip it expected_resp = '$command,{command},response: OK*'.format(command=command) # Check that the command has been correctly received by the GPS resp = self.serial.readline().decode('utf-8') if expected_resp not in resp: raise ValueError('Invalid reponse, got:\n {}'.format(resp)) if waitResp == True: resp = self.serial.readline().decode('utf-8') return resp def get_best_position(self) -> Union[None, BestPosition]: bestpos = self.send_command("LOG BESTPOSA once") if bestpos.startswith("#BESTPOSA") is False: return None if "INSUFFICIENT_OBS" in bestpos: return None # Get every field (separators are ',', ';', '*') parser = r"([^,\*;]+)" matches = re.findall(parser, bestpos) # Example of valid data: "#BESTPOSA,COM2,0,86.0,FINE,1991,402429.800,00000000,929861713,18;SOL_COMPUTED,NARROW_FLOAT,48.58252933494,7.76681014722,144.8847,48.4450,WGS84,0.1256,0.1388,0.3033,"0",0.800,0.000,11,10,10,5,0,00,0,13*d7c1d0b0" # Documentation on the BESTPOSA command can be found on the previous command manual # TODO: better & safer matching if len(matches) is not 32: return None gps_week = int(float(matches[5])) gps_sec = float(matches[6]) datetimeformat = "%Y-%m-%dT%H:%M:%S.%f" epoch = datetime.datetime.strptime("1980-01-06T00:00:00.000", datetimeformat) elapsed = datetime.timedelta(days=gps_week*7, seconds=gps_sec) # Warning: the following date doesn't take account of leap seconds! (substract 16 seconds @ 2018-06-11) gps_date = datetime.datetime.strftime(epoch + elapsed, datetimeformat) best_position = BestPosition( gps_week = gps_week, gps_sec = gps_sec, solution_type = matches[10], position_type = matches[11], gps_latitude = matches[12], gps_longitude = matches[13], gps_altitude = matches[14], gps_date=gps_date ) return best_position if __name__ == '__main__': rover = UB4B0_rover(port='/dev/ttyUSB0') while True: t = time() try: best_position = rover.get_best_position() print(best_position) except ValueError as e: print("Invalid data received: {}".format(e)) # Wait 1 second between two measurements # TODO: use PPS signal to check when new position is available if time() - t < 1: sleep(1 - (time() - t))