Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Quectel LC29H-BS #432

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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