Skip to content

Commit

Permalink
Add support for Quectel LC29H-BS
Browse files Browse the repository at this point in the history
Signed-off-by: Alastair D'Silva <[email protected]>
  • Loading branch information
deece committed Sep 18, 2024
1 parent 6a5a7ad commit f94daec
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 4 deletions.
15 changes: 15 additions & 0 deletions configure_gps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash
#
# configure_gps.sh: script to provide GPS modules with commands
# that are not saved in flash on the module (ie. they must be provided
# each time the module is started).


BASEDIR="$(dirname "$0")"
source <( grep -v '^#' "${BASEDIR}"/settings.conf | grep '=' ) #import settings

if [[ "${receiver}" = "Quectel LC29HBS" ]]; then
speed="${com_port_settings%%:*}"
python3 "${BASEDIR}"/tools/nmea.py --file "${BASEDIR}"/receiver_cfg/LC29HBS_Configure.txt /dev/"${com_port}" "${speed}" 3
echo Configuring Quectel LC29HBS on /dev/"${com_port}" at speed "${speed}"
fi
35 changes: 35 additions & 0 deletions receiver_cfg/LC29HBS_Configure.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#Enable MSM7 messages
$PAIR432,1

#Enable Station Reference Message 1005
$PAIR434,1

#Enable Ephemeris messages
$PAIR436,1

#Enable NMEA GGA Time, position, and fix related data
$PAIR062,0,1

#Enable NMEA GLL Position data: position fix, time of position fix, and status
$PAIR062,1,1

#Enable NMEA GSA GPS DOP and active satellites
$PAIR062,2,1

#Enable NMEA GSV Satellite information
$PAIR062,3,1

#Enable NMEA RMC Position, velocity, and time
$PAIR062,4,1

#Enable NMEA VTG Track made good and speed over ground
$PAIR062,5,1

#Enable NMEA ZDA UTC day, month, and year, and local time zone offset
$PAIR062,6,1

#Enable NMEA GRS GRS range residuals
$PAIR062,7,1

#Enable NMEA GST Position error statistics
$PAIR062,8,1
2 changes: 2 additions & 0 deletions receiver_cfg/LC29HBS_Factory_Defaults.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Restore factory defaults
$PQTMRESTOREPAR
9 changes: 9 additions & 0 deletions receiver_cfg/LC29HBS_Reboot.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#Power off the GNSS
$PAIR003

#SLEEP# 1000

#Power on the GNSS
$PAIR002

#SLEEP# 5000
2 changes: 2 additions & 0 deletions receiver_cfg/LC29HBS_Save.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#Save parameters
$PQTMSAVEPAR
2 changes: 2 additions & 0 deletions receiver_cfg/LC29HBS_Set_Baud.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#Crank the baud rate up (Could also go 3000000)
$PAIR864,0,0,921600
2 changes: 2 additions & 0 deletions receiver_cfg/LC29HBS_Version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Get the model and firmware version
$PQTMVERNO
36 changes: 33 additions & 3 deletions tools/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -421,13 +421,23 @@ detect_gnss() {
echo '################################'
systemctl is-active --quiet str2str_tcp.service && sudo systemctl stop str2str_tcp.service && echo 'Stopping str2str_tcp service'
for port in ttyS1 serial0 ttyS2 ttyS3 ttyS0; do
for port_speed in 115200 57600 38400 19200 9600; do
for port_speed in 3000000 921600 115200 57600 38400 19200 9600; do
echo 'DETECTION ON ' $port ' at ' $port_speed
# Detect u-blox ZED-F9P receivers
if [[ $(python3 "${rtkbase_path}"/tools/ubxtool -f /dev/$port -s $port_speed -p MON-VER -w 5 2>/dev/null) =~ 'ZED-F9P' ]]; then
detected_gnss[0]=$port
detected_gnss[1]='u-blox'
detected_gnss[2]=$port_speed
#echo 'U-blox ZED-F9P DETECTED ON '$port $port_speed
#echo 'U-blox ZED-F9P DETECTED ON ' $port ' at ' $port_speed
break
fi

# Detect Quectel LC29H-BS receivers using nmea.py
if [[ $(python3 "${rtkbase_path}"/tools/nmea.py --file "${rtkbase_path}"/receiver_cfg/LC29HBS_Version.txt /dev/$port $port_speed 3 2>/dev/null) =~ 'LC29HBS' ]]; then
detected_gnss[0]=$port
detected_gnss[1]='LC29H-BS'
detected_gnss[2]=$port_speed
#echo 'Quectel LC29H-BS DETECTED ON ' $port ' at ' $port_speed
break
fi
sleep 1
Expand Down Expand Up @@ -536,11 +546,30 @@ configure_gnss(){
sudo -u "${RTKBASE_USER}" sed -i s/^receiver=.*/receiver=\'Septentrio_Mosaic-X5\'/ "${rtkbase_path}"/settings.conf && \
sudo -u "${RTKBASE_USER}" sed -i s/^receiver_format=.*/receiver_format=\'sbf\'/ "${rtkbase_path}"/settings.conf
return $?
elif [[ $(python3 "${rtkbase_path}"/tools/nmea.py --file "${rtkbase_path}"/receiver_cfg/LC29HBS_Version.txt /dev/"${com_port}" ${com_port_settings%%:*} 3 2>/dev/null) =~ 'LC29HBS' ]]; then
# Factory reset and configure the module
python3 "${rtkbase_path}"/tools/nmea.py --verbose --file "${rtkbase_path}"/receiver_cfg/LC29HBS_Factory_Defaults.txt /dev/"${com_port}" ${com_port_settings%%:*} 3 >>"${rtkbase_path}"/logs/LC29HBS_Configure.log && \
python3 "${rtkbase_path}"/tools/nmea.py --verbose --file "${rtkbase_path}"/receiver_cfg/LC29HBS_Set_Baud.txt /dev/"${com_port}" ${com_port_settings%%:*} 3 >>"${rtkbase_path}"/logs/LC29HBS_Configure.log && \
python3 "${rtkbase_path}"/tools/nmea.py --verbose --file "${rtkbase_path}"/receiver_cfg/LC29HBS_Save.txt /dev/"${com_port}" ${com_port_settings%%:*} 3 >>"${rtkbase_path}"/logs/LC29HBS_Configure.log && \
python3 "${rtkbase_path}"/tools/nmea.py --verbose --file "${rtkbase_path}"/receiver_cfg/LC29HBS_Reboot.txt /dev/"${com_port}" ${com_port_settings%%:*} 3 >>"${rtkbase_path}"/logs/LC29HBS_Configure.log && \

# Speed has now been configured to 921600
speed=921600
version_str="$(python3 "${rtkbase_path}"/tools/nmea.py --file "${rtkbase_path}"/receiver_cfg/LC29HBS_Version.txt /dev/"${com_port}" ${speed} 3 2>/dev/null)"
firmware="`echo "$version_str" | cut -d , -f 2`"
if [[ -z "$version_str" ]]; then
echo "Could not get LC29HBS version string after rebooting the module, try power cycling the module."
return 1
fi
sudo -u "${RTKBASE_USER}" sed -i s/^receiver_firmware=.*/receiver_firmware=\'${firmware}\'/ "${rtkbase_path}"/settings.conf && \
sudo -u "${RTKBASE_USER}" sed -i s/^com_port_settings=.*/com_port_settings=\'921600:8:n:1\'/ "${rtkbase_path}"/settings.conf && \
sudo -u "${RTKBASE_USER}" sed -i s/^receiver=.*/receiver=\'Quectel LC29HBS\'/ "${rtkbase_path}"/settings.conf && \
sudo -u "${RTKBASE_USER}" sed -i s/^receiver_format=.*/receiver_format=\'rtcm3\'/ "${rtkbase_path}"/settings.conf
return $?
else
echo 'Failed to configure the Gnss receiver'
return 1
fi

else
echo 'No Gnss receiver has been set. We can'\''t configure'
return 1
Expand Down Expand Up @@ -611,6 +640,7 @@ start_services() {
systemctl daemon-reload
systemctl enable --now rtkbase_web.service
systemctl enable --now str2str_tcp.service
systemctl enable --now configure_gps.service
systemctl restart gpsd.service
systemctl restart chrony.service
systemctl enable --now rtkbase_archive.timer
Expand Down
107 changes: 107 additions & 0 deletions tools/nmea.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env python3

import argparse
import serial
import time

# Function to calculate NMEA checksum
def calculate_nmea_checksum(nmea_sentence):
checksum = 0
# Iterate through each character after the starting '$' and before '*'
for char in nmea_sentence[1:]:
checksum ^= ord(char)
return f"{nmea_sentence}*{checksum:02X}"

# Function to append checksum if not provided
def append_checksum_if_missing(nmea_sentence):
if '*' not in nmea_sentence:
# Calculate and append checksum if '*' is missing
return calculate_nmea_checksum(nmea_sentence)
return nmea_sentence

# Function to handle serial communication
def send_nmea_command(port, speed, timeout, nmea_command, verbose):
# Open serial port
try:
with serial.Serial(port, baudrate=speed, timeout=timeout) as ser:
# Append checksum if missing
nmea_command_with_checksum = append_checksum_if_missing(nmea_command)

# Write NMEA command to the serial port
ser.write((nmea_command_with_checksum + '\r\n').encode('ascii'))
if verbose:
print(f"Sent command: {nmea_command_with_checksum}")

# Wait for the response
start_time = time.time()
while time.time() - start_time < timeout:
try:
response = ser.readline().decode('ascii', errors='ignore').strip()
# Process only valid ASCII responses
if response and response.startswith('$'):
if verbose:
print(f"Received response: {response}")
return response
except UnicodeDecodeError as e:
# Skip non-ASCII responses (likely RTCM3 messages)
if verbose:
print(f"Non-ASCII data skipped: {e}")
if verbose:
print("Timeout: No matching response received.")
except serial.SerialException as e:
print(f"Error opening serial port: {e}")

# Function to read NMEA commands from a file and ignore lines starting with '#' and blank lines
def read_commands_from_file(file_path):
try:
with open(file_path, 'r') as file:
commands = []
for line in file:
line = line.strip()
# Ignore blank lines and lines starting with '#'
if line and not line.startswith('#') or line.startswith('#SLEEP#'):
commands.append(line)
return commands
except FileNotFoundError:
print(f"Error: File '{file_path}' not found.")
return []

# Function to handle the sleep command in the file
def handle_sleep_command(command, verbose):
try:
sleep_time_ms = int(command.split('#SLEEP# ')[1])
if verbose:
print(f"Sleeping for {sleep_time_ms} ms")
time.sleep(sleep_time_ms / 1000) # Convert to seconds
except (IndexError, ValueError):
print(f"Invalid sleep command format: {command}")

if __name__ == "__main__":
# Parse command line arguments
parser = argparse.ArgumentParser(description="Send NMEA commands to Quectel LC29H module")
parser.add_argument('port', type=str, help='Serial port to use (e.g., /dev/ttyUSB0 or COM3)')
parser.add_argument('speed', type=int, help='Baud rate (e.g., 9600)')
parser.add_argument('timeout', type=int, help='Timeout in seconds')
parser.add_argument('command', nargs='?', type=str, help='NMEA command to send (optional, overrides file)')
parser.add_argument('--file', type=str, help='File with NMEA commands to send')
parser.add_argument('--verbose', action='store_true', help='Enable verbose output for tracing')

args = parser.parse_args()

# Determine which commands to send (from file or argument)
if args.command:
# Send the provided command as an argument
nmea_commands = [args.command]
elif args.file:
# Read commands from the file, ignoring comments and blank lines
nmea_commands = read_commands_from_file(args.file)
else:
print("Error: You must provide either a command or a file containing commands.")
exit(1)

# Send each NMEA command from the list
for command in nmea_commands:
if command.startswith('#SLEEP#'):
handle_sleep_command(command, args.verbose)
else:
send_nmea_command(args.port, args.speed, args.timeout, command, args.verbose)
3 changes: 2 additions & 1 deletion tools/uninstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ for service_name in str2str_tcp.service \
rtkbase_archive.timer \
modem_check.service \
modem_check.timer \
rtkbase_gnss_web_proxy.service
rtkbase_gnss_web_proxy.service \
configure_gps.service
do
echo 'Deleting ' "${service_name}"
systemctl stop "${service_name}"
Expand Down
19 changes: 19 additions & 0 deletions unit/configure_gps.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[Unit]
Description=Configure GPS
Before=str2str_tcp.service
#After=network-online.target
#Wants=network-online.target
#Requires=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
User={user}
ExecStart={script_path}/configure_gps.sh
Restart=no
ProtectHome=read-only
ProtectSystem=strict
ReadWritePaths={script_path}

[Install]
WantedBy=multi-user.target
1 change: 1 addition & 0 deletions web_app/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
{'service_unit' : 'rtkbase_archive.timer', "name" : "archive_timer"},
{'service_unit' : 'rtkbase_archive.service', "name" : "archive_service"},
{'service_unit' : 'rtkbase_raw2nmea.service', "name" : "raw2nmea"},
{'service_unit' : 'configure_gps.service', "name" : "configure_gps"},
]

#Delay before rtkrcv will stop if no user is on status.html page
Expand Down

0 comments on commit f94daec

Please sign in to comment.