Skip to content

Lg290p support - #557

Open
S7ud wants to merge 4 commits into
Stefal:masterfrom
S7ud:lg290p-support
Open

Lg290p support#557
S7ud wants to merge 4 commits into
Stefal:masterfrom
S7ud:lg290p-support

Conversation

@S7ud

@S7ud S7ud commented Aug 15, 2026

Copy link
Copy Markdown

added support for LG290P receiver
tested with Waveshare module firmware version LG290P03AANR01A06S

S7ud added 4 commits August 15, 2026 11:22
lg290p_tool.py mirrors unicore_tool.py/sept_tool.py's CLI shape (detect,
get_model/firmware, reset, save, send_config_file) so it can be wired into
install.sh's detect_gnss()/configure_gnss() with minimal changes. Detection
is content-based (queries PQTMVERNO) rather than USB VID:PID, since the
CH343 USB-serial bridge used on LG290P breakout boards has no vendor-
identifying string and the same VID:PID appears on unrelated hardware.

receiver_cfg/LG290P_rtkbase_rtcm3.cfg configures base-station mode, MSM7
RTCM3 observations, ephemeris, and station position (RTCM 1005). Verified
end-to-end against real hardware, including a working RTK Fixed/Float
solution on a separate physical rover. The config's comments document
several LG290P firmware quirks found and worked around during testing:
a save/reset ordering bug affecting PQTMCFGRTCM/PQTMCFGMSGRATE writes made
in the same cycle as a receiver-mode change, a first-ephemeris-broadcast
delay tied to PQTMCFGRTCM's EPH_Interval, and unreliable internal
survey-in convergence (falls back to fixed-position mode).

tools/rtcm_sniff.py is a small standalone CRC-24Q-validated RTCM3 message
counter, used throughout testing to verify receiver and caster output
independent of any specific consumer.
detect_gnss(): probes for an LG290P via lg290p_tool.py --detect as a last
resort, after the existing u-blox/Septentrio USB matching and the F9P/
Unicore UART loop, so it never delays or writes unsolicited commands to
other receivers already handled by the existing detection paths.

configure_gnss(): new branch matching get_model == 'LG290P', following the
same shape as the Unicore branch - factory reset, push
receiver_cfg/LG290P_rtkbase_rtcm3.cfg, then record receiver/format/port/
min_free_space in settings.conf. No udev rule was added for /dev/ttyGNSS:
the USB-serial bridge's VID:PID is shared with unrelated hardware, so
detection is deliberately content-based instead (see lg290p_tool.py).

No changes needed in web_app/ - receiver_format 'rtcm3' and the receiver/
model fields are already handled generically there.
Found while getting LG290P time sync working, but applies generally:
gpsd can't get a time fix from a raw rtcm3/sbf/etc stream (only NMEA),
and raw2nmea.sh already has the logic to skip itself for ubx receivers
(which gpsd can read directly) - it just never had an automatic enable
path for anyone. This wires it into start_services() the same way
modem_check.timer and rtkbase_gnss_web_proxy.service are already
conditionally enabled there, so Unicore benefits too, not just LG290P.

No dedicated web UI toggle exists for this service (unlike the local
NTRIP caster and file-logging services, which already have one), so
this was previously a manual systemctl step for every non-ubx receiver.
Operationalizes the fixed-position fallback the base config already
documents as a manual option: converts a real position to WGS84 ECEF
and pushes it to the receiver as PQTMCFGSVIN fixed mode, then saves and
resets (a plain save alone doesn't apply a PQTMCFGSVIN change - see the
cfg file's own notes on this).

Two ways to get the position:
  - Live-averaged from rtkbase_raw2nmea's NMEA output over a configurable
    duration (default case - no external dependency needed).
  - Supplied directly via --llh or --ecef, for feeding in a result from a
    post-processing service (AUSPOS, OPUS, etc.) once enough raw data has
    been logged for a precise fix - skips live averaging entirely.

Verified end-to-end against real hardware: both input paths produce
matching ECEF output, and the receiver's own PQTMCFGSVIN,R readback
confirms the position is correctly applied after each run.

Note: this only fixes what the RECEIVER broadcasts (RTCM 1005).
RTKBase's "Base coordinates" setting is a separate value fed to
str2str's -p flag, which does not reliably produce a working 1005 for
this receiver in its relay/filter configuration - see the base config's
own notes on this. Making the web UI push a receiver's fixed position
automatically on save is worth doing, but needs a receiver-agnostic
design (every vendor has its own fixed-mode command) - out of scope
here, left as a follow-up.
@S7ud

S7ud commented Aug 15, 2026

Copy link
Copy Markdown
Author

##Setup for testing

LG290P base station — setup runbook

For a fresh Pi + fresh RTKBase install + factory-reset LG290P. Everything up through step 3
is automated; step 4 isn't (and some of it can't be, for reasons noted inline).

1. Install RTKBase

install.sh --all repo can't reach a fork (it's hardcoded to github.com/stefal/rtkbase), so
clone directly instead:

cd ~
git clone https://github.com/S7ud/rtkbase.git
grep -q '^rtkbase_path=' /etc/environment || echo 'rtkbase_path=/home/<user>/rtkbase' | sudo tee -a /etc/environment
sudo env rtkbase_path=/home/<user>/rtkbase rtkbase/tools/install.sh -d -r -q -t -g

(-d dependencies, -r RTKLIB, -q Python requirements, -t unit files, -g gpsd/chrony.)

2. Detect and configure the receiver

Connect the LG290P over USB, gpsd/str2str_tcp not yet running (they aren't at this point).

cd ~/rtkbase
sudo env rtkbase_path=~/rtkbase tools/install.sh --detect-gnss --configure-gnss

This factory-resets the receiver and pushes receiver_cfg/LG290P_rtkbase_rtcm3.cfg — base
mode, MSM7, ephemeris, 1005 enabled, survey-in as the default position source. Confirm it
printed Quectel LG290P successfuly configured before moving on.

3. Start services

sudo tools/install.sh -s

Starts rtkbase_web, str2str_tcp, gpsd, chrony, the archive timer, and (as of this
branch) rtkbase_raw2nmea automatically — needed for gpsd to get a time fix at all, since the
receiver's own NMEA has empty position fields in base mode and the RTCM3-tagged relay strips
NMEA out anyway.

Open the web UI (http://<pi-ip>) from here on.

4. Manual steps — do these every time, they don't survive a fresh install

a. Enable the outputs you want, via their toggle switches in Settings (not systemctl
these have proper UI switches): "Ntrip Caster service" for the local caster your rover
connects to, "File service" if you want local RTCM3 logging.

b. Clean up the caster's message list. The default local_ntripc_msg is a generic
everything-list (legacy 1004/1006/1008/1012, unsupported 1107/1230, redundant 1005+1006).
Trim it to what the LG290P actually emits — in Settings, under the Ntrip Caster options, set
the message field to:

1077,1087,1097,1127,1005(10),1033

c. Set the receiver's actual base position. Survey-in (the default) is unreliable on this
receiver — confirmed on real hardware, matches independent Quectel forum reports. Don't wait
on it. Use the fixed-position script instead:

# quick start - averages ~5 min of live single-point fixes from raw2nmea
./tools/lg290p_set_fixed_position.py --port /dev/ttyACM0

# once you have a precise position from a post-processing service (AUSPOS/OPUS/etc),
# after logging raw data for a while - replaces the quick-start value
./tools/lg290p_set_fixed_position.py --port /dev/ttyACM0 --ecef <X> <Y> <Z>

Verify it landed: ./tools/lg290p_tool.py --port /dev/ttyACM0 --command raw 'PQTMCFGSVIN,R'
should show your real coordinates, not 0.0000,0.0000,0.0000.

d. Optionally, copy the same position into "Base coordinates" in Settings too — cosmetic
(map display), the script above is what the rover actually depends on. See the fixed-position
section of receiver_cfg/LG290P_rtkbase_rtcm3.cfg for why these are separate.

@charper-imdex charper-imdex mentioned this pull request Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant