diff --git a/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvProfile.java b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvProfile.java new file mode 100644 index 00000000..0e4af6a5 --- /dev/null +++ b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvProfile.java @@ -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; +} diff --git a/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvProfileMapper.java b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvProfileMapper.java new file mode 100644 index 00000000..11de77f1 --- /dev/null +++ b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvProfileMapper.java @@ -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 { +} + diff --git a/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSession.java b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSession.java new file mode 100644 index 00000000..c2e1072c --- /dev/null +++ b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSession.java @@ -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; +} diff --git a/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSessionMapper.java b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSessionMapper.java new file mode 100644 index 00000000..6a7eb0ac --- /dev/null +++ b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSessionMapper.java @@ -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 { +} + diff --git a/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSolution.java b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSolution.java new file mode 100644 index 00000000..1389435c --- /dev/null +++ b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSolution.java @@ -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; +} + diff --git a/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSolutionMapper.java b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSolutionMapper.java new file mode 100644 index 00000000..0e20058d --- /dev/null +++ b/sec-api/src/main/java/com/imdroid/secapi/dto/RtkrcvSolutionMapper.java @@ -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 { +} + diff --git a/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/executor/D331RtcmMessageExecutor.java b/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/executor/D331RtcmMessageExecutor.java index ff66573e..e8bf4a19 100644 --- a/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/executor/D331RtcmMessageExecutor.java +++ b/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/executor/D331RtcmMessageExecutor.java @@ -16,6 +16,7 @@ import com.imdroid.sideslope.server.DeviceChannel; import com.imdroid.sideslope.server.OnlineChannels; import com.imdroid.sideslope.service.DataPersistService; import com.imdroid.sideslope.bd.RtcmGgaUtil; +import com.imdroid.sideslope.rtkcluster.RtkClusterService; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.slf4j.Logger; @@ -48,6 +49,8 @@ public class D331RtcmMessageExecutor implements Executor UdpNtripServer ntripServer; @Autowired private GNSSDataCalcService gnssCalcService; + @Autowired + private RtkClusterService rtkClusterService; // 添加一个成员变量用于追踪每个测站最后一次转发D300数据的时间 private final Map lastD300ForwardTimeMap = new ConcurrentHashMap<>(); @@ -124,8 +127,16 @@ public class D331RtcmMessageExecutor implements Executor byte[] srcdata = message.getSrcData(); String rtcm = ByteUtil.bytesToHexString(srcdata); sendToNtrip(id, rtcm); + + try { + rtkClusterService.sendRtcm(id, rtcm); + } catch (Exception e) { + logger.debug("send to rtkCluster failed for {}: {}", id, e.getMessage()); + } } + + ThreadManager.getFixedThreadPool().submit(() -> { // 原始码流输出到日志文件 -- INFO 级别 // 只有测站开了日志记录,或者消息来自基站,才将原码记录到日志文件 diff --git a/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/ntrip/RtkrcvConfigService.java b/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/ntrip/RtkrcvConfigService.java new file mode 100644 index 00000000..fafbaa14 --- /dev/null +++ b/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/ntrip/RtkrcvConfigService.java @@ -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 lines = readTemplateLines(); + + List 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 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 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; + } +} diff --git a/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/rtkcluster/RtkClusterService.java b/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/rtkcluster/RtkClusterService.java new file mode 100644 index 00000000..1cd2594a --- /dev/null +++ b/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/rtkcluster/RtkClusterService.java @@ -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 endpoints = new ConcurrentHashMap<>(); + private final Map 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 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 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) {} + } + } +} diff --git a/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/service/Device.java b/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/service/Device.java index 73197a14..d395ded0 100644 --- a/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/service/Device.java +++ b/sec-beidou-rtcm/src/main/java/com/imdroid/sideslope/service/Device.java @@ -100,6 +100,9 @@ public class Device { byte dataChannelType = CHANNEL_TYPE_UDP; // 0:TCP;1:DUP int lasQuality = 0; + // rtkrcv cluster work port (127.0.0.1:port) + Integer rtkrcvWorkPort; + public void updateRx(int head, int bytes,int count){ lastRxHead = head; diff --git a/sec-beidou-rtcm/src/main/resources/rtkrcv_default.conf b/sec-beidou-rtcm/src/main/resources/rtkrcv_default.conf new file mode 100644 index 00000000..517496de --- /dev/null +++ b/sec-beidou-rtcm/src/main/resources/rtkrcv_default.conf @@ -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 =