Compare commits

..

2 Commits

Author SHA1 Message Date
efa2cacd3b feat: 新增 RtkClusterService 2025-10-28 14:06:35 +08:00
bf9efff854 feat: 新增 RtkClusterService 2025-10-28 14:06:18 +08:00
12 changed files with 737 additions and 0 deletions

View File

@ -0,0 +1,29 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("rtkrcv_profile")
public class RtkrcvProfile {
@TableId(value = "device_id", type = IdType.INPUT)
private String deviceId;
@TableField("inpstr1_path")
private String inpstr1Path;
@TableField("inpstr2_path")
private String inpstr2Path;
@TableField("inpstr3_path")
private String inpstr3Path;
@TableField("outstr1_path")
private String outstr1Path;
@TableField("out_height")
private Integer outHeight;
@TableField("updated_at")
private LocalDateTime updatedAt;
}

View File

@ -0,0 +1,9 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RtkrcvProfileMapper extends BaseMapper<RtkrcvProfile> {
}

View File

@ -0,0 +1,52 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("rtkrcv_session")
public class RtkrcvSession {
@TableId(value = "session_id", type = IdType.AUTO)
private Long sessionId;
@TableField("device_id")
private String deviceId;
@TableField("state")
private String state;
@TableField("rtk_conf_path")
private String rtkConfPath;
@TableField("work_dir")
private String workDir;
@TableField("work_port")
private Integer workPort;
@TableField("pid")
private Integer pid;
@TableField("start_time")
private LocalDateTime startTime;
@TableField("end_time")
private LocalDateTime endTime;
@TableField("heartbeat_at")
private LocalDateTime heartbeatAt;
@TableField("error_message")
private String errorMessage;
@TableField("created_at")
private LocalDateTime createdAt;
@TableField("updated_at")
private LocalDateTime updatedAt;
}

View File

@ -0,0 +1,9 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RtkrcvSessionMapper extends BaseMapper<RtkrcvSession> {
}

View File

@ -0,0 +1,51 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("rtkrcv_solution")
public class RtkrcvSolution {
@TableId(value = "solution_id", type = IdType.AUTO)
private Long solutionId;
@TableField("device_id")
private String deviceId;
@TableField("lat")
private Double lat;
@TableField("lon")
private Double lon;
@TableField("height")
private Double height;
@TableField("ecef_x")
private Double ecefX;
@TableField("ecef_y")
private Double ecefY;
@TableField("ecef_z")
private Double ecefZ;
@TableField("quality")
private String quality;
@TableField("duration_s")
private Integer durationSeconds;
@TableField("obs_count")
private Integer obsCount;
@TableField("created_at")
private LocalDateTime createdAt;
}

View File

@ -0,0 +1,9 @@
package com.imdroid.secapi.dto;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RtkrcvSolutionMapper extends BaseMapper<RtkrcvSolution> {
}

View File

@ -16,6 +16,7 @@ import com.imdroid.sideslope.server.DeviceChannel;
import com.imdroid.sideslope.server.OnlineChannels; import com.imdroid.sideslope.server.OnlineChannels;
import com.imdroid.sideslope.service.DataPersistService; import com.imdroid.sideslope.service.DataPersistService;
import com.imdroid.sideslope.bd.RtcmGgaUtil; import com.imdroid.sideslope.bd.RtcmGgaUtil;
import com.imdroid.sideslope.rtkcluster.RtkClusterService;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -48,6 +49,8 @@ public class D331RtcmMessageExecutor implements Executor<D331RtcmMessage, Void>
UdpNtripServer ntripServer; UdpNtripServer ntripServer;
@Autowired @Autowired
private GNSSDataCalcService gnssCalcService; private GNSSDataCalcService gnssCalcService;
@Autowired
private RtkClusterService rtkClusterService;
// 添加一个成员变量用于追踪每个测站最后一次转发D300数据的时间 // 添加一个成员变量用于追踪每个测站最后一次转发D300数据的时间
private final Map<String, Long> lastD300ForwardTimeMap = new ConcurrentHashMap<>(); private final Map<String, Long> lastD300ForwardTimeMap = new ConcurrentHashMap<>();
@ -124,7 +127,15 @@ public class D331RtcmMessageExecutor implements Executor<D331RtcmMessage, Void>
byte[] srcdata = message.getSrcData(); byte[] srcdata = message.getSrcData();
String rtcm = ByteUtil.bytesToHexString(srcdata); String rtcm = ByteUtil.bytesToHexString(srcdata);
sendToNtrip(id, rtcm); sendToNtrip(id, rtcm);
try {
rtkClusterService.sendRtcm(id, rtcm);
} catch (Exception e) {
logger.debug("send to rtkCluster failed for {}: {}", id, e.getMessage());
} }
}
ThreadManager.getFixedThreadPool().submit(() -> { ThreadManager.getFixedThreadPool().submit(() -> {
// 原始码流输出到日志文件 -- INFO 级别 // 原始码流输出到日志文件 -- INFO 级别

View File

@ -0,0 +1,110 @@
package com.imdroid.sideslope.ntrip;
import com.imdroid.secapi.dto.RtkrcvProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Service
public class RtkrcvConfigService {
private static final Logger LOGGER = LoggerFactory.getLogger(RtkrcvConfigService.class);
@Value("${rtkrcv.workdir:/opt/rtk}")
private String defaultWorkDir;
/**
* Generate an RTKLIB rtkrcv config file from template using profile values.
* The file will be saved to `${rtkrcv.workdir:/opt/rtk}/rtkrcv_{deviceId}.conf`.
*/
public Path generateConfig(RtkrcvProfile profile) throws IOException {
return generateConfig(profile, java.nio.file.Paths.get(defaultWorkDir));
}
/**
* Generate an RTKLIB rtkrcv config file from the classpath template `rtkrcv_default.conf`.
* Only the following keys are substituted: inpstr1-path, inpstr2-path, inpstr3-path, out-height.
* The output filename will be `rtkrcv_{deviceId}.conf` under the provided outputDir.
*/
public Path generateConfig(RtkrcvProfile profile, Path outputDir) throws IOException {
if (profile == null) throw new IllegalArgumentException("profile must not be null");
if (profile.getDeviceId() == null || profile.getDeviceId().trim().isEmpty()) {
throw new IllegalArgumentException("profile.deviceId must not be empty");
}
List<String> lines = readTemplateLines();
List<String> rendered = new ArrayList<>(lines.size());
for (String line : lines) {
String replaced = line;
replaced = replaceValueLine(replaced, "inpstr1-path", nz(profile.getInpstr1Path()));
replaced = replaceValueLine(replaced, "inpstr2-path", nz(profile.getInpstr2Path()));
replaced = replaceValueLine(replaced, "inpstr3-path", nz(profile.getInpstr3Path()));
replaced = replaceValueLine(replaced, "outstr1-path", nz(profile.getOutstr1Path()));
Integer outHeight = profile.getOutHeight();
int heightValue = (outHeight == null) ? 1 : (outHeight == 0 ? 0 : 1);
replaced = replaceValueLine(replaced, "out-height", String.valueOf(heightValue));
rendered.add(replaced);
}
if (!Files.exists(outputDir)) {
Files.createDirectories(outputDir);
}
Path out = outputDir.resolve("rtkrcv_" + profile.getDeviceId() + ".conf");
try (BufferedWriter writer = Files.newBufferedWriter(out, StandardCharsets.UTF_8)) {
for (String l : rendered) {
writer.write(l);
writer.newLine();
}
}
LOGGER.info("Generated rtkrcv config: {}", out);
return out;
}
private List<String> readTemplateLines() throws IOException {
ClassPathResource resource = new ClassPathResource("rtkrcv_default.conf");
if (!resource.exists()) {
throw new IOException("Template rtkrcv_default.conf not found on classpath");
}
List<String> lines = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
}
return lines;
}
private String nz(String s) {
return s == null ? "" : s;
}
/**
* Replace the value of a line like: "key = value # comment" while preserving spacing and comment.
*/
private String replaceValueLine(String line, String key, String newValue) {
String regex = "^(\\s*" + key + "\\s*=\\s*)([^#]*?)(\\s*(#.*)?)$";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(line);
if (!m.find()) return line;
String prefix = m.group(1);
String suffix = m.group(3) == null ? "" : m.group(3);
String value = newValue == null ? "" : newValue.trim();
return prefix + value + suffix;
}
}

View File

@ -0,0 +1,286 @@
package com.imdroid.sideslope.rtkcluster;
import com.imdroid.common.util.ByteUtil;
import com.imdroid.secapi.dto.RtkrcvProfile;
import com.imdroid.secapi.dto.RtkrcvProfileMapper;
import com.imdroid.secapi.dto.RtkrcvSession;
import com.imdroid.secapi.dto.RtkrcvSessionMapper;
import com.imdroid.sideslope.ntrip.RtkrcvConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
/**
* RtkCluster: per-device TCP server on a single port number for both IN (RTCM to rtkrcv) and OUT (NMEA from rtkrcv).
* - For each deviceId, we create a local server bound to 127.0.0.1:workPort.
* - rtkrcv connects twice to the same port: one connection will be classified as OUT (it sends data quickly),
* the other as IN (no initial data; we will write RTCM to it).
*/
@Service
public class RtkClusterService implements ApplicationRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(RtkClusterService.class);
@Value("${rtk.cluster.basePort:24000}")
private int basePort;
@Value("${rtkrcv.workdir:/opt/rtk}")
private String rtkWorkDir;
@Autowired
private RtkrcvProfileMapper profileMapper;
@Autowired
private RtkrcvSessionMapper sessionMapper;
@Autowired
private RtkrcvConfigService configService;
private final Map<String, DeviceEndpoint> endpoints = new ConcurrentHashMap<>();
private final Map<String, Process> processes = new ConcurrentHashMap<>();
private final ExecutorService worker = Executors.newCachedThreadPool(r -> {
Thread t = new Thread(r, "rtkcluster-worker");
t.setDaemon(true);
return t;
});
@Override
public void run(ApplicationArguments args) {
// Load all profiles and bootstrap endpoints and sessions.
List<RtkrcvProfile> profiles = profileMapper.selectList(null);
if (profiles == null || profiles.isEmpty()) {
LOGGER.info("No rtkrcv_profile records found. RtkCluster idle.");
return;
}
int slot = 1;
for (RtkrcvProfile profile : profiles) {
try {
int port = basePort + slot++;
bootstrapDevice(profile, port);
} catch (Exception e) {
LOGGER.error("Bootstrap device {} failed: {}", profile.getDeviceId(), e.getMessage(), e);
}
}
}
private void bootstrapDevice(RtkrcvProfile profile, int workPort) throws IOException {
String deviceId = profile.getDeviceId();
// 1) Start endpoint server (if not exists)
endpoints.computeIfAbsent(deviceId, k -> new DeviceEndpoint(workPort));
DeviceEndpoint ep = endpoints.get(deviceId);
ep.ensureStarted();
// 2) Update profile inp/out to local cluster port
String localPath = "127.0.0.1:" + workPort;
profile.setInpstr1Path(localPath);
profile.setOutstr1Path(localPath);
profileMapper.updateById(profile);
// 3) Generate config and start rtkrcv
Path conf = configService.generateConfig(profile);
// 4) Insert session with rtk_conf_path present
RtkrcvSession session = new RtkrcvSession();
session.setDeviceId(deviceId);
session.setState("starting");
session.setWorkDir(rtkWorkDir);
session.setWorkPort(workPort);
session.setRtkConfPath(conf.toString());
session.setCreatedAt(LocalDateTime.now());
session.setUpdatedAt(LocalDateTime.now());
sessionMapper.insert(session);
startRtkrcv(deviceId, conf.getParent().toString(), conf.toString());
}
private void startRtkrcv(String deviceId, String workDir, String confPath) {
if (processes.containsKey(deviceId)) {
Process p = processes.get(deviceId);
if (p != null && p.isAlive()) {
LOGGER.warn("rtkrcv for {} already running", deviceId);
return;
}
}
worker.submit(() -> {
try {
ProcessBuilder builder = new ProcessBuilder("rtkrcv", "-nc", "-o", confPath);
builder.directory(new File(workDir));
builder.redirectErrorStream(true);
LOGGER.info("Starting rtkrcv for {} with {}", deviceId, confPath);
Process p = builder.start();
processes.put(deviceId, p);
// async log stdout
logOutput(deviceId, p);
} catch (IOException e) {
LOGGER.error("Failed to start rtkrcv for {}: {}", deviceId, e.getMessage(), e);
}
});
}
private void logOutput(String deviceId, Process p) {
worker.submit(() -> {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
LOGGER.info("[rtkrcv:{}] {}", deviceId, line);
}
} catch (IOException e) {
LOGGER.warn("Error reading rtkrcv output for {}: {}", deviceId, e.getMessage());
} finally {
try {
int code = p.waitFor();
LOGGER.info("rtkrcv for {} exited with {}", deviceId, code);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
}
public void sendRtcm(String deviceId, String hexRtcm) {
DeviceEndpoint ep = endpoints.get(deviceId);
if (ep == null) return;
byte[] data = ByteUtil.hexStringTobyte(hexRtcm);
ep.enqueueRtcm(data);
}
static class DeviceEndpoint {
private final int port;
private final ExecutorService exec;
private volatile boolean started = false;
private volatile ServerSocket server;
private volatile Socket inConn; // server writes RTCM to this
private volatile Socket outConn; // server reads NMEA from this
private final BlockingQueue<byte[]> rtcmQueue = new LinkedBlockingQueue<>(1024);
DeviceEndpoint(int port) {
this.port = port;
this.exec = Executors.newCachedThreadPool(r -> {
Thread t = new Thread(r, "rtkcluster-ep-" + this.port);
t.setDaemon(true);
return t;
});
}
synchronized void ensureStarted() {
if (started) return;
exec.submit(this::acceptLoop);
exec.submit(this::dequeueLoop);
started = true;
}
void enqueueRtcm(byte[] data) {
if (data == null || data.length == 0) return;
rtcmQueue.offer(data);
}
private void acceptLoop() {
try (ServerSocket ss = new ServerSocket()) {
ss.setReuseAddress(true);
ss.bind(new InetSocketAddress("127.0.0.1", port));
this.server = ss;
LOGGER.info("RtkCluster device endpoint listening on 127.0.0.1:{}", port);
while (true) {
Socket s = ss.accept();
classifyConnection(s);
}
} catch (IOException e) {
LOGGER.error("Device endpoint on {} stopped: {}", port, e.getMessage(), e);
}
}
private void classifyConnection(Socket s) {
exec.submit(() -> {
try {
s.setSoTimeout(300);
InputStream in = s.getInputStream();
byte[] probe = new byte[64];
int n = 0;
try { n = in.read(probe); } catch (IOException ignore) {}
if (n > 0 && isLikelyText(probe, n)) {
// OUT connection (NMEA etc.)
closeQuietly(outConn);
outConn = s;
LOGGER.info("Endpoint {} OUT connected", port);
pumpOut(outConn);
} else {
// IN connection (RTCM sink)
closeQuietly(inConn);
inConn = s;
LOGGER.info("Endpoint {} IN connected", port);
}
} catch (IOException e) {
LOGGER.warn("classifyConnection error: {}", e.getMessage());
closeQuietly(s);
}
});
}
private boolean isLikelyText(byte[] buf, int n) {
int printable = 0;
for (int i = 0; i < n; i++) {
int b = buf[i] & 0xFF;
if (b >= 32 && b <= 126) printable++;
}
return printable >= Math.max(1, n - 4); // tolerate some non-printables
}
private void pumpOut(Socket s) {
exec.submit(() -> {
try (InputStream in = s.getInputStream()) {
byte[] buf = new byte[2048];
int read;
while ((read = in.read(buf)) != -1) {
// For now, just log a short preview
String preview = new String(buf, 0, Math.min(read, 64), StandardCharsets.US_ASCII).replaceAll("\n", "\\n");
LOGGER.info("[OUT:{}] {} bytes: {}...", port, read, preview);
}
} catch (IOException e) {
LOGGER.info("OUT connection closed on {}: {}", port, e.getMessage());
} finally {
if (outConn == s) outConn = null;
closeQuietly(s);
}
});
}
private void dequeueLoop() {
while (true) {
try {
byte[] data = rtcmQueue.take();
Socket sink = inConn;
if (sink == null || sink.isClosed()) continue;
try {
OutputStream os = sink.getOutputStream();
os.write(data);
os.flush();
} catch (IOException e) {
LOGGER.warn("Write RTCM failed on {}: {}", port, e.getMessage());
closeQuietly(sink);
inConn = null;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
private void closeQuietly(Socket s) {
if (s == null) return;
try { s.close(); } catch (IOException ignore) {}
}
}
}

View File

@ -100,6 +100,9 @@ public class Device {
byte dataChannelType = CHANNEL_TYPE_UDP; // 0:TCP;1:DUP byte dataChannelType = CHANNEL_TYPE_UDP; // 0:TCP;1:DUP
int lasQuality = 0; int lasQuality = 0;
// rtkrcv cluster work port (127.0.0.1:port)
Integer rtkrcvWorkPort;
public void updateRx(int head, int bytes,int count){ public void updateRx(int head, int bytes,int count){
lastRxHead = head; lastRxHead = head;

View File

@ -39,6 +39,12 @@ warning.log.directory=./log
ntrip_server.port = 11100 ntrip_server.port = 11100
rtkrcv.enable=true
rtkrcv.binary=rtkrcv
rtkrcv.config=/opt/rtk/rtk.conf
rtkrcv.workdir=/opt/rtk
rtk.tcp.port=20001
sim.url = http://120.78.169.220:8089 sim.url = http://120.78.169.220:8089
sim.username = gzyzdz sim.username = gzyzdz
sim.key = 632629d1269a202c9d49a574623e4e4c sim.key = 632629d1269a202c9d49a574623e4e4c

View File

@ -0,0 +1,162 @@
pos1-posmode =single # (0:single,1:dgps,2:kinematic,3:static,4:static-start,5:movingbase,6:fixed,7:ppp-kine,8:ppp-static,9:ppp-fixed)
pos1-frequency =l1+l2+l5 # (1:l1,2:l1+l2,3:l1+l2+l5,4:l1+l2+l5+l6)
pos1-soltype =forward # (0:forward,1:backward,2:combined,3:combined-nophasereset)
pos1-elmask =10 # (deg)
pos1-snrmask_r =on # (0:off,1:on)
pos1-snrmask_b =on # (0:off,1:on)
pos1-snrmask_L1 =0,0,0,0,0,0,0,0,0
pos1-snrmask_L2 =0,0,0,0,0,0,0,0,0
pos1-snrmask_L5 =0,0,0,0,0,0,0,0,0
pos1-dynamics =off # (0:off,1:on)
pos1-tidecorr =off # (0:off,1:on,2:otl)
pos1-ionoopt =brdc # (0:off,1:brdc,2:sbas,3:dual-freq,4:est-stec,5:ionex-tec,6:qzs-brdc)
pos1-tropopt =saas # (0:off,1:saas,2:sbas,3:est-ztd,4:est-ztdgrad)
pos1-sateph =brdc+sbas # (0:brdc,1:precise,2:brdc+sbas,3:brdc+ssrapc,4:brdc+ssrcom)
pos1-posopt1 =off # (0:off,1:on)
pos1-posopt2 =off # (0:off,1:on)
pos1-posopt3 =off # (0:off,1:on,2:precise)
pos1-posopt4 =off # (0:off,1:on)
pos1-posopt5 =off # (0:off,1:on)
pos1-posopt6 =off # (0:off,1:on)
pos1-exclsats = # (prn ...)
pos1-navsys =45 # (1:gps+2:sbas+4:glo+8:gal+16:qzs+32:bds+64:navic)
pos2-armode =continuous # (0:off,1:continuous,2:instantaneous,3:fix-and-hold)
pos2-gloarmode =fix-and-hold # (0:off,1:on,2:autocal,3:fix-and-hold)
pos2-bdsarmode =on # (0:off,1:on)
pos2-arfilter =on # (0:off,1:on)
pos2-arthres =3
pos2-arthresmin =2
pos2-arthresmax =2
pos2-arthres1 =0.1
pos2-arthres2 =0
pos2-arthres3 =1e-09
pos2-arthres4 =1e-05
pos2-varholdamb =0.1 # (cyc^2)
pos2-gainholdamb =0.01
pos2-arlockcnt =5
pos2-minfixsats =4
pos2-minholdsats =5
pos2-mindropsats =10
pos2-arelmask =10 # (deg)
pos2-arminfix =20
pos2-armaxiter =1
pos2-elmaskhold =10 # (deg)
pos2-aroutcnt =20
pos2-maxage =30 # (s)
pos2-syncsol =off # (0:off,1:on)
pos2-slipthres =0.05 # (m)
pos2-dopthres =0 # (m)
pos2-rejionno =5 # (m)
pos2-rejcode =30 # (m)
pos2-niter =1
pos2-baselen =0 # (m)
pos2-basesig =0 # (m)
out-solformat =llh # (0:llh,1:xyz,2:enu,3:nmea)
out-outhead =on # (0:off,1:on)
out-outopt =on # (0:off,1:on)
out-outvel =off # (0:off,1:on)
out-timesys =gpst # (0:gpst,1:utc,2:jst)
out-timeform =hms # (0:tow,1:hms)
out-timendec =3
out-degform =deg # (0:deg,1:dms)
out-fieldsep =
out-outsingle =off # (0:off,1:on)
out-maxsolstd =0 # (m)
out-height =1 # (0:ellipsoidal,1:geodetic)
out-geoid =internal # (0:internal,1:egm96,2:egm08_2.5,3:egm08_1,4:gsi2000)
out-solstatic =all # (0:all,1:single)
out-nmeaintv1 =0 # (s)
out-nmeaintv2 =0 # (s)
out-outstat =residual # (0:off,1:state,2:residual)
stats-eratio1 =300
stats-eratio2 =300
stats-eratio5 =300
stats-errphase =0.003 # (m)
stats-errphaseel =0.003 # (m)
stats-errphasebl =0 # (m/10km)
stats-errdoppler =1 # (Hz)
stats-snrmax =52 # (dB.Hz)
stats-errsnr =0 # (m)
stats-errrcv =0 # ( )
stats-stdbias =30 # (m)
stats-stdiono =0.03 # (m)
stats-stdtrop =0.3 # (m)
stats-prnaccelh =3 # (m/s^2)
stats-prnaccelv =1 # (m/s^2)
stats-prnbias =0.0001 # (m)
stats-prniono =0.001 # (m)
stats-prntrop =0.0001 # (m)
stats-prnpos =0 # (m)
stats-clkstab =5e-12 # (s/s)
ant1-postype =llh # (0:llh,1:xyz,2:single,3:posfile,4:rinexhead,5:rtcm,6:raw)
ant1-pos1 =90 # (deg|m)
ant1-pos2 =0 # (deg|m)
ant1-pos3 =-6335367.6285 # (m|m)
ant1-anttype =
ant1-antdele =0 # (m)
ant1-antdeln =0 # (m)
ant1-antdelu =0 # (m)
ant2-postype =rtcm # (0:llh,1:xyz,2:single,3:posfile,4:rinexhead,5:rtcm,6:raw)
ant2-pos1 =0 # (deg|m)
ant2-pos2 =0 # (deg|m)
ant2-pos3 =0 # (m|m)
ant2-anttype =
ant2-antdele =0 # (m)
ant2-antdeln =0 # (m)
ant2-antdelu =0 # (m)
ant2-maxaveep =1
ant2-initrst =on # (0:off,1:on)
misc-timeinterp =off # (0:off,1:on)
misc-sbasatsel =0 # (0:all)
misc-rnxopt1 =
misc-rnxopt2 =
misc-pppopt =
file-satantfile =
file-rcvantfile =
file-staposfile =
file-geoidfile =
file-ionofile =
file-dcbfile =
file-eopfile =
file-blqfile =
file-tempdir =./RTKNAVI
file-geexefile =
file-solstatfile =
file-tracefile =
#
inpstr1-type =ntripcli # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,6:ntripcli,7:ftp,8:http)
inpstr2-type =ntripcli # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,6:ntripcli,7:ftp,8:http)
inpstr3-type =ntripcli # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,6:ntripcli,7:ftp,8:http)
inpstr1-path =beidou:29832611@8.134.185.53:8001/6539837
#inpstr2-path =cedr25866:fyx25455@120.253.226.97:8001/RTCM33_GRCEJ
inpstr2-path =ytcors14847:fyx25943@gnss.ytcors.cn:8003/RTCM33GRCEJpro
#inpstr2-path =beidou:29832611@192.168.100.206:2101/8507903
inpstr3-path =Ming:@Zhang12345@ntrip.data.gnss.ga.gov.au:2101/BCEP00BKG0
inpstr1-format =rtcm3 # (0:rtcm2,1:rtcm3,2:oem4,4:ubx,5:swift,6:hemis,7:skytraq,8:javad,9:nvs,10:binex,11:rt17,12:sbf,15:sp3)
inpstr2-format =rtcm3 # (0:rtcm2,1:rtcm3,2:oem4,4:ubx,5:swift,6:hemis,7:skytraq,8:javad,9:nvs,10:binex,11:rt17,12:sbf,15:sp3)
inpstr3-format =rtcm3 # (0:rtcm2,1:rtcm3,2:oem4,4:ubx,5:swift,6:hemis,7:skytraq,8:javad,9:nvs,10:binex,11:rt17,12:sbf,15:sp3)
inpstr2-nmeareq =single # (0:off,1:latlon,2:single)
inpstr2-nmealat = # (deg)
inpstr2-nmealon = # (deg)
outstr1-type =tcpcli # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,5:ntripsvr,9:ntripcas)
outstr2-type =off # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,5:ntripsvr,9:ntripcas)
outstr1-path =127.0.0.1:20001
outstr2-path =
outstr1-format =llh # (0:llh,1:xyz,2:enu,3:nmea:4:stat)
outstr2-format = # (0:llh,1:xyz,2:enu,3:nmea:4:stat)
logstr1-type =0 # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,5:ntripsvr,9:ntripcas)
logstr2-type =off # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,5:ntripsvr,9:ntripcas)
logstr3-type =off # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,5:ntripsvr,9:ntripcas)
logstr1-path =
logstr2-path =
logstr3-path =
misc-svrcycle =10 # (ms)
misc-timeout =10000 # (ms)
misc-reconnect =10000 # (ms)
misc-nmeacycle =5000 # (ms)
misc-buffsize =32768 # (bytes)
misc-navmsgsel =all # (0:all,1:rover,2:base,3:corr)
misc-proxyaddr =
misc-fswapmargin =30 # (s)
console-passwd =