{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# 安装 gnss_lib_py\n!pip install gnss-lib-py -q","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-06-11T01:24:37.057584Z","iopub.execute_input":"2026-06-11T01:24:37.057860Z","iopub.status.idle":"2026-06-11T01:24:52.556284Z","shell.execute_reply.started":"2026-06-11T01:24:37.057837Z","shell.execute_reply":"2026-06-11T01:24:52.555353Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport gnss_lib_py as glp\nimport matplotlib.pyplot as plt\n\nprint(\"环境配置完成，gnss_lib_py 已加载。\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-11T01:24:52.558094Z","iopub.execute_input":"2026-06-11T01:24:52.558403Z","iopub.status.idle":"2026-06-11T01:24:54.011094Z","shell.execute_reply.started":"2026-06-11T01:24:52.558367Z","shell.execute_reply":"2026-06-11T01:24:54.010505Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 数据集根目录（需先在右侧 Add Data 挂载数据集）\nDATA_ROOT = \"/kaggle/input/competitions/smartphone-decimeter-2023/sdc2023/\"\n\n# 读取数据\nsub = pd.read_csv(DATA_ROOT + \"sample_submission.csv\")\nsub.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-11T01:24:54.011881Z","iopub.execute_input":"2026-06-11T01:24:54.012246Z","iopub.status.idle":"2026-06-11T01:24:54.134501Z","shell.execute_reply.started":"2026-06-11T01:24:54.012228Z","shell.execute_reply":"2026-06-11T01:24:54.133824Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 选取一个包含 L1/L5 信号的典型 Trace 进行实验\n# 路径格式：train/[日期-场景]/[手机型号]/...\nTRACE_DIR = os.path.join(DATA_ROOT, \"train/2022-05-13-20-57-us-ca-mtv-pe1/pixel6pro\")\nGNSS_PATH = os.path.join(TRACE_DIR, \"device_gnss.csv\")\nGT_PATH = os.path.join(TRACE_DIR, \"ground_truth.csv\")\n\nprint(f\"实验数据路径: {GNSS_PATH}\")\ngnss_data = pd.read_csv(GNSS_PATH, low_memory=False)\ngnss_data.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-11T01:24:54.135879Z","iopub.execute_input":"2026-06-11T01:24:54.136102Z","iopub.status.idle":"2026-06-11T01:24:55.444889Z","shell.execute_reply.started":"2026-06-11T01:24:54.136081Z","shell.execute_reply":"2026-06-11T01:24:55.444344Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 打印所有列名（表头）\nprint(\"字段总数:\", len(gnss_data.columns))\nprint(\"=\" * 50)\nfor i, col in enumerate(gnss_data.columns):    \n    print(f\"{i+1:>2}. {col}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-11T01:24:55.445622Z","iopub.execute_input":"2026-06-11T01:24:55.445802Z","iopub.status.idle":"2026-06-11T01:24:55.450018Z","shell.execute_reply.started":"2026-06-11T01:24:55.445784Z","shell.execute_reply":"2026-06-11T01:24:55.449348Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"\n### **第一组：消息标识与接收机时钟（字段 1-11）**\n\n这一组对应 Android `GnssClock` 类，用于将手机硬件时钟归算到统一的 GPS 时间。\n\n| # | 字段名 | 单位 | 含义与算法用途 |\n|---|--------|------|----------------|\n| 1 | MessageType | 字符串 | 消息类型，原始观测固定为 `Raw` |\n| 2 | utcTimeMillis | 毫秒 | 距 Unix 纪元的 UTC 时间戳，用于与真值时间对齐 |\n| 3 | TimeNanos | 纳秒 | 接收机硬件时钟计数，是计算 GPS 时间的原始量 |\n| 4 | LeapSecond | 秒 | 闰秒（GPS 与 UTC 差值，当前为 18 秒） |\n| 5 | TimeUncertaintyNanos | 纳秒 | 时间测量不确定度 |\n| 6 | FullBiasNanos | 纳秒 | 硬件时钟与 GPS 起点的整体偏差（时间归算核心项） |\n| 7 | BiasNanos | 纳秒 | 亚纳秒级微小偏差，与 FullBiasNanos 叠加使用 |\n| 8 | BiasUncertaintyNanos | 纳秒 | 钟差不确定度 |\n| 9 | DriftNanosPerSecond | 纳秒/秒 | 接收机钟漂（钟速），影响多普勒测速精度 |\n| 10 | DriftUncertaintyNanosPerSecond | 纳秒/秒 | 钟漂不确定度 |\n| 11 | HardwareClockDiscontinuityCount | 整数 | 硬件时钟不连续计数，跳变时载波相位连续性中断（**周跳标志**） |\n\n### **第二组：卫星标识与信号到达时间（字段 12-16）**\n\n这一组是 Android 计算伪距的最底层量。伪距本质上由\"信号到达时间 $-$ 信号发射时间\"乘光速得到。\n\n| # | 字段名 | 单位 | 含义与算法用途 |\n|---|--------|------|----------------|\n| 12 | Svid | 整数 | 卫星编号（PRN），配合星座类型唯一标识一颗卫星 |\n| 13 | TimeOffsetNanos | 纳秒 | 测量值相对于时钟时刻的微小偏移 |\n| 14 | State | 位标志 | 卫星同步状态码，判断 TOW（周内时）是否成功解码，决定伪距是否可用 |\n| 15 | ReceivedSvTimeNanos | 纳秒 | 接收到的卫星发射时间，伪距计算的关键输入 |\n| 16 | ReceivedSvTimeUncertaintyNanos | 纳秒 | 上者的不确定度，可用于剔除低质量观测 |\n\n### **第三组：信号质量与多普勒（字段 17-19, 27-31）**\n\n这一组是你**动态抗差权重**和 **SNR 掩模**两大创新点的直接数据来源。\n\n| # | 字段名 | 单位 | 含义与算法用途 |\n|---|--------|------|----------------|\n| 17 | Cn0DbHz | dB-Hz | **载噪比（核心指标）**，衡量信号强度，用于 SNR 加权与掩模 |\n| 18 | PseudorangeRateMetersPerSecond | 米/秒 | 伪距变化率（由多普勒转换），用于测速与 TDCP 切换备用 |\n| 19 | PseudorangeRateUncertaintyMetersPerSecond | 米/秒 | 伪距率不确定度，测速随机模型权重 |\n| 27 | MultipathIndicator | 整数 | 多路径指示（0 未知 / 1 检测到 / 2 未检测到） |\n| 28 | SnrInDb | dB | 信噪比（与 Cn0 不同，多数手机此列为空） |\n| 29 | ConstellationType | 整数 | 星座类型（1:GPS, 3:GLONASS, 5:QZSS, 6:Galileo, 等） |\n| 30 | AgcDb | dB | 自动增益控制电平，反映外部射频干扰强度 |\n| 31 | BasebandCn0DbHz | dB-Hz | 基带载噪比（Android 11+），比 Cn0DbHz 更贴近真实质量 |\n\n### **第四组：载波相位 / 累积增量范围（字段 20-26）**\n\n这一组是你 **TDCP 历元间约束**的核心，决定轨迹平滑度。\n\n| # | 字段名 | 单位 | 含义与算法用途 |\n|---|--------|------|----------------|\n| 20 | AccumulatedDeltaRangeState | 位标志 | **ADR 状态码（最关键）**，判断载波是否有效、是否发生周跳/半周模糊 |\n| 21 | AccumulatedDeltaRangeMeters | 米 | **累积增量范围（即载波相位观测）**，TDCP 计算位移增量的直接输入 |\n| 22 | AccumulatedDeltaRangeUncertaintyMeters | 米 | ADR 不确定度，突增即提示周跳，用于剔除 |\n| 23 | CarrierFrequencyHz | Hz | 载波频率，**区分 L1（约 1575.42 MHz）与 L5（约 1176.45 MHz）的核心依据** |\n| 24 | CarrierCycles | 周 | 完整载波周数（API 28 后已弃用，通常为空） |\n| 25 | CarrierPhase | 周 | 载波相位小数部分（已弃用，通常为空） |\n| 26 | CarrierPhaseUncertainty | 周 | 载波相位不确定度（已弃用，通常为空） |\n\n### **第五组：硬件偏差与信号编码（字段 32-37）**\n\n这一组主要用于多频多系统的偏差对齐。\n\n| # | 字段名 | 单位 | 含义与算法用途 |\n|---|--------|------|----------------|\n| 32 | FullInterSignalBiasNanos | 纳秒 | 完整系统间信号偏差，多星座/多频联合解算时需对齐 |\n| 33 | FullInterSignalBiasUncertaintyNanos | 纳秒 | 上者不确定度 |\n| 34 | SatelliteInterSignalBiasNanos | 纳秒 | 卫星端信号间偏差 |\n| 35 | SatelliteInterSignalBiasUncertaintyNanos | 纳秒 | 上者不确定度 |\n| 36 | CodeType | 字符串 | 测距码类型（如 C/A、Q、I 等） |\n| 37 | ChipsetElapsedRealtimeNanos | 纳秒 | 芯片组自启动以来的真实时间，用于传感器时间同步 |\n\n### **第六组：派生伪距与卫星几何状态（字段 38-50）**\n\n从此组开始为 **Google 预计算的派生字段**，已帮你完成伪距构造和卫星定轨，这是 Derived 数据集\"友好\"之处。\n\n| # | 字段名 | 单位 | 含义与算法用途 |\n|---|--------|------|----------------|\n| 38 | ArrivalTimeNanosSinceGpsEpoch | 纳秒 | 自 GPS 纪元起的信号到达时间（已归算） |\n| 39 | RawPseudorangeMeters | 米 | **原始伪距观测值**，定位方程左端项（即代码中的 `raw_pr_m`） |\n| 40 | RawPseudorangeUncertaintyMeters | 米 | 伪距不确定度，WLS 随机模型基础权重 |\n| 41 | SignalType | 字符串 | 信号类型（如 `GPS_L1`、`GPS_L5`、`GAL_E1`），双频筛选标志 |\n| 42 | ReceivedSvTimeNanosSinceGpsEpoch | 纳秒 | 自 GPS 纪元起的卫星发射时间 |\n| 43 | SvPositionXEcefMeters | 米 | **卫星 ECEF 坐标 X**，定位方程已知点 |\n| 44 | SvPositionYEcefMeters | 米 | 卫星 ECEF 坐标 Y |\n| 45 | SvPositionZEcefMeters | 米 | 卫星 ECEF 坐标 Z |\n| 46 | SvElevationDegrees | 度 | **卫星仰角**，用于仰角掩模与加权（低仰角多路径严重） |\n| 47 | SvAzimuthDegrees | 度 | 卫星方位角，可用于天空图绘制与 NLOS 判断 |\n| 48 | SvVelocityXEcefMetersPerSecond | 米/秒 | 卫星速度矢量 X，多普勒/测速解算用 |\n| 49 | SvVelocityYEcefMetersPerSecond | 米/秒 | 卫星速度矢量 Y |\n| 50 | SvVelocityZEcefMetersPerSecond | 米/秒 | 卫星速度矢量 Z |\n\n### **第七组：误差修正模型与基准解（字段 51-58）**\n\n这一组提供了各类系统误差修正项，以及 Google 官方基准解，用于你的精度对比。\n\n| # | 字段名 | 单位 | 含义与算法用途 |\n|---|--------|------|----------------|\n| 51 | SvClockBiasMeters | 米 | **卫星钟差修正**，修正伪距必加项 |\n| 52 | SvClockDriftMetersPerSecond | 米/秒 | 卫星钟漂，测速修正 |\n| 53 | IsrbMeters | 米 | 系统间距离偏置（Inter-Signal Range Bias），多星座基准对齐 |\n| 54 | IonosphericDelayMeters | 米 | **电离层延迟修正**（L1/L5 双频组合可替代此模型值） |\n| 55 | TroposphericDelayMeters | 米 | **对流层延迟修正** |\n| 56 | WlsPositionXEcefMeters | 米 | **Google 官方 WLS 基准解 X**（你的算法对比基准） |\n| 57 | WlsPositionYEcefMeters | 米 | Google 官方 WLS 基准解 Y |\n| 58 | WlsPositionZEcefMeters | 米 | Google 官方 WLS 基准解 Z |\n\n### **针对你算法与论文的重点提示**\n\n把全部 58 个字段对应到你的 U-Hybrid 算法四大模块，核心字段其实非常集中。**修正后伪距**的构造用到 39、51、54、55，公式为 `corr_pr = RawPseudorangeMeters + SvClockBiasMeters - IonosphericDelayMeters - TroposphericDelayMeters`；**定位几何**用 43-45（卫星坐标）配合 46（仰角加权）；**TDCP 平滑模块**只盯 21、22、20 三个字段，其中字段 20 的 ADR 状态码是判断周跳的生命线；**动态抗差与 SNR 掩模**则依赖 17（载噪比）和 39（伪距）；**双频识别**用 23 或 41 区分 L1/L5；**性能验证**直接拿你的解算结果与 56-58 三列 Google 基准作差。\n\n需要注意一个实操细节：字段 24、25、26（CarrierCycles、CarrierPhase、CarrierPhaseUncertainty）自 Android API 28 起已被官方弃用，绝大多数手机这三列为空，**你做载波相位处理时务必使用字段 21 的 AccumulatedDeltaRangeMeters，而不是字段 25 的 CarrierPhase**，否则会取到全空值。这是很多复现者容易踩的坑。","metadata":{}},{"cell_type":"code","source":"def load_and_clean_data(csv_path):\n    # AndroidDerived2023 会自动将你的列名映射为标准名\n    # 例如：Cn0DbHz -> cn0_dbhz, CarrierFrequencyHz -> carrier_frequency_hz\n    navdata = glp.AndroidDerived2023(csv_path)\n    print(navdata)\n    \n    # 物理层筛选：剔除信噪比低于 28 的弱信号\n    filtered = navdata.where(\"cn0_dbhz\", 28, condition=\"greater\")\n    print(filtered[\"CarrierFrequencyHz\"])\n    \n    # 双频识别逻辑：在论文中你可以提到你优先保留了 L5 频段的观测\n    # L5 频率约为 1.176e9 Hz\n    # 用 1.3e9 作为 L1/L5 的分界阈值\n    # carrier_frequency_hz 载波频率，区分 L1（约 1575.42 MHz）与 L5（约 1176.45 MHz）的核心依据\n    L5_FREQ_THRESHOLD = 1.3e9\n    l5_mask = filtered[\"CarrierFrequencyHz\"] < L5_FREQ_THRESHOLD\n    print(f\"总观测数: {len(navdata)}, 过滤后: {len(filtered)}, 其中 L5 信号占比: {np.sum(l5_mask)/len(filtered):.1%}\")\n    \n    return filtered\n\nmeasurements = load_and_clean_data(GNSS_PATH)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-11T01:24:55.450839Z","iopub.execute_input":"2026-06-11T01:24:55.451042Z","iopub.status.idle":"2026-06-11T01:24:58.006337Z","shell.execute_reply.started":"2026-06-11T01:24:55.451025Z","shell.execute_reply":"2026-06-11T01:24:58.005621Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"navdata = glp.AndroidDerived2023(GNSS_PATH)\nprint(type(navdata), dir(navdata))\n\n# 转换为 pandas DataFrame 后查看列名\ndf = navdata.pandas_df()\nprint(\"字段总数:\", len(df.columns))\nprint(\"=\" * 50)\nfor i, col in enumerate(df.columns):\n    print(f\"{i+1}: {col}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-11T01:24:58.007191Z","iopub.execute_input":"2026-06-11T01:24:58.007411Z","iopub.status.idle":"2026-06-11T01:25:00.196845Z","shell.execute_reply.started":"2026-06-11T01:24:58.007393Z","shell.execute_reply":"2026-06-11T01:25:00.195704Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"`glp.AndroidDerived2023`会大幅扩充原始数据，新增许多字段。这主要是因为它内置了一系列数据清洗、误差修正和质量增强的算法。\n\n下表列出了一些关键的增强操作及其新生成的字段：\n\n| 处理阶段 | 核心目的 / 操作 | 主要新增字段 |\n| :--- | :--- | :--- |\n| **1. 列名与格式标准化** | 将CSV中原始（如`TimeNanos`）映射为统一的内部字段（如`gps_millis`）。 | `gps_millis`, `raw_pr_m` |\n| **2. 物理量计算与数据扩充** | 利用卫星星历（Broadcast Ephemeris）等辅助文件，计算生成新的物理量字段。 | `sv_clock_bias_m`, `sat_pos_x/y/z_m` 等等 |\n| **3. 伪距修正 (`postprocess`)** | 通过精密算法消除测量值（伪距）中的系统误差。 | `pr_m` (修正后的伪距) |\n| **4. 星座类型映射** | 把代码（如`1`）转换为`gnss_id`（如`G`），以方便按星座进行数据分析。 | `gnss_id` (GNSS identifier) |\n| **5. 仰角/方位角计算** | 结合接收机和卫星位置，计算出仰角和方位角，用于DOP计算、抗多径分析等。 | `el_deg`, `az_deg` |\n\n以上是`AndroidDerived2023`自动完成的核心步骤。在实际研究中，你通常只需要直接使用这些“派生”出的新字段进行建模和分析即可。\n\n### 💎 总结\n`glp.AndroidDerived2023` 的强大之处在于，它将复杂且易错的数据清洗、物理量计算和误差修正等预处理步骤封装了起来，让你能直接专注于算法研发和定位结果的优化，无需再为琐碎的数据细节分心。","metadata":{}},{"cell_type":"code","source":"def apply_corrections(navdata):\n    # 伪距修正：原始伪距 + 卫星钟差 - 电离层 - 对流层\n    navdata[\"corr_pr_full_m\"] = (\n        navdata[\"raw_pr_m\"]        # 原始伪距 (字段48)\n        + navdata[\"b_sv_m\"]        # 卫星钟差修正 (字段46)，带符号，直接相加\n        - navdata[\"iono_delay_m\"]  # 减去电离层延迟 (字段51)\n        - navdata[\"tropo_delay_m\"] # 减去对流层延迟 (字段52)\n    )\n    \n    return navdata\n\nprint(measurements)\ncorrected_measurements = apply_corrections(measurements)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-11T01:41:17.134559Z","iopub.execute_input":"2026-06-11T01:41:17.134890Z","iopub.status.idle":"2026-06-11T01:41:17.959666Z","shell.execute_reply.started":"2026-06-11T01:41:17.134871Z","shell.execute_reply":"2026-06-11T01:41:17.958902Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def add_tdcp_smoothing(navdata):\n    # TDCP 利用的是载波相位的变化量\n    # 在 GSDC 派生数据中，这对应于 accumulated_delta_range_m\n    # 论文点：通过检测 adr_uncertainty 剔除周跳卫星\n    smooth_data = navdata.where(\"adr_uncertainty_m\", 0.1, \"<\")\n    \n    # 计算历元间的位移增量\n    # 这里我们为 WLS 提供一个更小的先验方差\n    print(f\"TDCP 有效观测卫星数: {len(smooth_data)}\")\n    return smooth_data\n\n# 为保持演示简洁，我们先进行鲁棒权重的 WLS 求解\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}