0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RON-V #12 — GNSSとLTE測定をSQLiteへ保存し、Grafana Geomapの準備をする

0
Last updated at Posted at 2026-09-23

RON-V 連載リンク
← 前の記事:#11 — Grafanaで観測データを可視化
→ 次の記事:#13 — Meshtastic 920MHz Mesh通信

[!NOTE]
2026-09-23 現行GitHub同期メモ
現在の実装は app/gnss/gpsd_reader.py、app/lte/measurement_store.py、app/lte/measure_once.py が正本です。GNSSはgpsd TCP (127.0.0.1:2947, timeout 5秒)、LTEはRTL-SDR Blog V4 + srsRAN cell_search を使用します。RFとLTEは同一SDRを共有するため /tmp/ronv-sdr.lock で排他し、現在はRF測定終了後にLTE測定を起動するsystemd構成です。Geomap用dashboardも現在は grafana/dashboards/ronv-mie-map.json としてGitHub管理されています。

はじめに

前回の #11 では、Raspberry Pi 5上のRON-VへGrafanaを導入し、

AtomS3R
   ↓
USB Serial
   ↓
SQLite
   ↓
Grafana SQLite Datasource
   ↓
Grafana

というデータ参照経路を構築しました。

実際に、

vehicle_events

テーブルをGrafanaから参照し、保存済みの車両イベントを表示できることを確認しました。

今回は、RON-Vのもう一つの重要な観測データである、

GNSS位置
    +
LTEセル情報

を組み合わせます。

最終的な目標は、

走行位置
   +
LTE Band
EARFCN
PCI
PSS power
PCI数
   ↓
SQLite
   ↓
Grafana Geomap

として、車両で走行しながら通信状況を地図上へ表示することです。

今回はその前段として、

GNSS
  +
LTE cell_search
  ↓
lte_measurements
  ↓
SQLite

まで構築します。


1. 今回の構成

使用環境は以下です。

Raspberry Pi 5

Debian GNU/Linux 13 (trixie)

arm64 / aarch64

RON-V
/home/ronv/ronv

Python venv
/home/ronv/.venvs/ronv

SQLite
/var/lib/ronv/ronv.db

GNSSは、

u-blox 7 USB GNSS Receiver

を使用しています。

LTE受信は、

RTL-SDR Blog V4
      ↓
SoapySDR
      ↓
srsRAN 4G
      ↓
cell_search

です。


2. 今回作るデータ経路

今回の処理は、

u-blox GNSS
     ↓
gpsd
     ↓
gpsd_reader.py
     │
     ├─ latitude
     ├─ longitude
     └─ gps_status
            │
            │
RTL-SDR Blog V4
     ↓
SoapySDR
     ↓
srsRAN cell_search
     │
     ├─ EARFCN
     ├─ PCI
     ├─ PRB
     ├─ ports
     └─ PSS power
            │
            ▼
      measure_once.py
            │
            ▼
      lte_measurements
            │
            ▼
     /var/lib/ronv/ronv.db
            │
            ▼
          Grafana

とします。


3. LTE測定テーブルを作成する

まず、既存のRON-V SQLiteをバックアップします。

SQLiteは稼働中でも .backup コマンドを使用できます。

cd /home/ronv/ronv

STAMP=$(date +%Y%m%d_%H%M%S)

sqlite3 /var/lib/ronv/ronv.db \
  ".backup '/var/lib/ronv/ronv_before_lte_${STAMP}.db'"

chmod 0660 \
  /var/lib/ronv/ronv_before_lte_${STAMP}.db

echo "===== BACKUP ====="

ls -lh \
  /var/lib/ronv/ronv_before_lte_${STAMP}.db

実機では、

28K

程度のバックアップDBが作成されました。


4. measurement_store.py を作成する

LTE測定データを保存するため、

app/lte/measurement_store.py

を作成します。

cd /home/ronv/ronv

cat > app/lte/measurement_store.py <<'PY'
from __future__ import annotations

import re
from datetime import datetime
from zoneinfo import ZoneInfo

from app.common.config import load_config
from app.common.db import (
    connect_db,
    initialize_database,
)


SCAN_PATTERN = re.compile(
    r"EARFCN\s+"
    r"(?P<earfcn>\d+)\s+"
    r"Freq\.\s+"
    r"(?P<frequency_mhz>"
    r"\d+(?:\.\d+)?"
    r")\s+MHz"
)


def initialize_lte_schema(
    config: dict | None = None,
) -> None:
    """
    Create tables used for RON-V LTE measurements.
    """

    cfg = config or load_config()

    con = connect_db(cfg)

    try:
        con.execute(
            """
            CREATE TABLE IF NOT EXISTS
            lte_measurements (
                id INTEGER PRIMARY KEY AUTOINCREMENT,

                timestamp_jst TEXT NOT NULL,

                band INTEGER NOT NULL,
                frequency_mhz REAL NOT NULL,
                earfcn INTEGER NOT NULL,

                pci INTEGER,
                prb INTEGER,
                ports INTEGER,
                pss_power_dbm REAL,

                pci_count INTEGER NOT NULL DEFAULT 0,

                latitude REAL,
                longitude REAL,
                gps_status TEXT NOT NULL,

                status TEXT NOT NULL,

                raw_log TEXT NOT NULL
            )
            """
        )

        con.execute(
            """
            CREATE INDEX IF NOT EXISTS
            idx_lte_measurements_timestamp
            ON lte_measurements(timestamp_jst)
            """
        )

        con.execute(
            """
            CREATE INDEX IF NOT EXISTS
            idx_lte_measurements_pci
            ON lte_measurements(pci)
            """
        )

        con.execute(
            """
            CREATE INDEX IF NOT EXISTS
            idx_lte_measurements_earfcn
            ON lte_measurements(earfcn)
            """
        )

        con.commit()

    finally:
        con.close()


def _scan_targets(
    raw_log: str,
) -> list[tuple[int, float]]:
    """
    Extract scanned EARFCN/frequency pairs
    from cell_search output.
    """

    targets: list[tuple[int, float]] = []

    for match in SCAN_PATTERN.finditer(raw_log):

        target = (
            int(match.group("earfcn")),
            float(
                match.group(
                    "frequency_mhz"
                )
            ),
        )

        if target not in targets:
            targets.append(target)

    return targets


def save_lte_measurements(
    cells: list[dict],
    raw_log: str,
    position: dict,
    config: dict | None = None,
) -> list[int]:
    """
    Store one LTE scan result.

    One row is stored for each detected PCI.

    If no cell is found, a no_cells row is
    stored for each scanned EARFCN.
    """

    cfg = config or load_config()

    timezone = ZoneInfo(
        cfg["project"]["timezone"]
    )

    timestamp_jst = datetime.now(
        timezone
    ).isoformat(
        timespec="milliseconds"
    )

    band = int(
        cfg["lte"]["band"]
    )

    latitude = position.get(
        "latitude"
    )

    longitude = position.get(
        "longitude"
    )

    gps_status = str(
        position.get(
            "gps_status",
            "unknown",
        )
    )

    pci_count = len(cells)

    rows: list[tuple] = []

    if cells:

        for cell in cells:

            rows.append(
                (
                    timestamp_jst,
                    band,
                    cell["frequency_mhz"],
                    cell["earfcn"],
                    cell["pci"],
                    cell["prb"],
                    cell["ports"],
                    cell["pss_power_dbm"],
                    pci_count,
                    latitude,
                    longitude,
                    gps_status,
                    "cell_found",
                    raw_log,
                )
            )

    else:

        targets = _scan_targets(
            raw_log
        )

        if not targets:
            raise ValueError(
                "No LTE scan target found "
                "in cell_search output"
            )

        for earfcn, frequency_mhz in targets:

            rows.append(
                (
                    timestamp_jst,
                    band,
                    frequency_mhz,
                    earfcn,
                    None,
                    None,
                    None,
                    None,
                    0,
                    latitude,
                    longitude,
                    gps_status,
                    "no_cells",
                    raw_log,
                )
            )

    con = connect_db(cfg)

    ids: list[int] = []

    try:

        for row in rows:

            cur = con.execute(
                """
                INSERT INTO lte_measurements (
                    timestamp_jst,
                    band,
                    frequency_mhz,
                    earfcn,
                    pci,
                    prb,
                    ports,
                    pss_power_dbm,
                    pci_count,
                    latitude,
                    longitude,
                    gps_status,
                    status,
                    raw_log
                )
                VALUES (
                    ?, ?, ?, ?, ?, ?, ?, ?,
                    ?, ?, ?, ?, ?, ?
                )
                """,
                row,
            )

            ids.append(
                int(cur.lastrowid)
            )

        con.commit()

    finally:
        con.close()

    return ids


def main() -> None:
    cfg = load_config()

    initialize_database(cfg)
    initialize_lte_schema(cfg)

    print(
        "LTE measurement schema : OK"
    )


if __name__ == "__main__":
    main()
PY

5. lte_measurements テーブル構造

作成するテーブルは、

lte_measurements

です。

保存項目は以下です。

column 内容
id レコードID
timestamp_jst Raspberry Pi測定時刻
band LTE Band
frequency_mhz 周波数
earfcn EARFCN
pci Physical Cell ID
prb Physical Resource Block
ports アンテナports
pss_power_dbm PSS power
pci_count 同一scanで検出したPCI数
latitude 緯度
longitude 経度
gps_status GNSS状態
status LTE測定状態
raw_log cell_search生ログ

Geomapで重要になるのは、

latitude
longitude

です。


6. LTE schemaを初期化する

Python構文を確認します。

cd /home/ronv/ronv

/home/ronv/.venvs/ronv/bin/python \
  -m py_compile \
  app/common/db.py \
  app/lte/measurement_store.py

何も表示されなければ正常です。

実行します。

/home/ronv/.venvs/ronv/bin/python \
  -m app.lte.measurement_store

実機では、

LTE measurement schema : OK

となりました。


7. SQLite schemaを確認する

sqlite3 /var/lib/ronv/ronv.db <<'SQL'
.headers on
.mode column

SELECT name
FROM sqlite_master
WHERE type = 'table'
ORDER BY name;

SELECT name
FROM sqlite_master
WHERE type = 'index'
  AND tbl_name = 'lte_measurements'
ORDER BY name;

PRAGMA journal_mode;
PRAGMA integrity_check;
SQL

実機では、

lte_measurements
ronv_meta
sqlite_sequence
vehicle_events

が確認できました。

LTE用INDEXは、

idx_lte_measurements_earfcn
idx_lte_measurements_pci
idx_lte_measurements_timestamp

です。

SQLite状態は、

journal_mode
------------
wal

integrity_check
---------------
ok

でした。


8. テーブル詳細を確認する

sqlite3 /var/lib/ronv/ronv.db \
  "PRAGMA table_info(lte_measurements);"

実機では、

id
timestamp_jst
band
frequency_mhz
earfcn
pci
prb
ports
pss_power_dbm
pci_count
latitude
longitude
gps_status
status
raw_log

を確認しました。


9. 初期状態は0件

まだLTE測定を保存していない状態では、

sqlite3 /var/lib/ronv/ronv.db <<'SQL'
.headers on
.mode column

SELECT
    COUNT(*) AS lte_measurements
FROM lte_measurements;
SQL

結果は、

lte_measurements
----------------
0

です。

SELECT ... はLinuxのbashコマンドではありません。

Tera Termなどの $ プロンプトへSQLだけを直接貼り付けるのではなく、上記のように sqlite3 <<'SQL' の中で実行します。


10. Grafanaからも新テーブルを確認する

Grafanaの、

Explore

を開きます。

Datasourceは、

RON-V SQLite

です。

SQL欄へ、

SELECT
    COUNT(*) AS lte_measurements
FROM lte_measurements;

を入力します。

実機では、

lte_measurements
0

となりました。

これでGrafanaからも新しいLTE測定テーブルを確認できました。


11. 現行のGNSS / LTE設定

実環境の config/ronv.toml はGit管理しません。公開テンプレート config/ronv.example.toml を基準に設定します。

[lte]
cell_search = "/home/ronv/srsRAN_4G/build/lib/examples/cell_search"
rf_device = "soapy"
rf_args = "driver=rtlsdr"
band = 28
earfcn_start = 9510
earfcn_end = 9511
gain_db = 43.9
timeout_sec = 60

[gnss]
host = "127.0.0.1"
port = 2947
timeout_sec = 5

[sdr]
lock_file = "/tmp/ronv-sdr.lock"
lock_timeout_sec = 5.0

現在は設定編集前に記事固有の .bak_before_gnss_lte を作る方式ではなく、GitHubのテンプレートと復旧手順を基準にします。

12. gpsdをTCPで読む

RON-VではGNSSの生Serialを複数プログラムから直接開くのではなく、

u-blox
   ↓
gpsd
   ↓
RON-V

として使用します。

gpsd標準portは、

2947

です。


13. GNSSモジュールを作成する

ディレクトリを作ります。

cd /home/ronv/ronv

mkdir -p \
  app/gnss

touch \
  app/gnss/__init__.py

次のファイルを作成します。

app/gnss/gpsd_reader.py
cat > app/gnss/gpsd_reader.py <<'PY'
from __future__ import annotations

import json
import socket
import time

from app.common.config import load_config


def get_gnss_position(
    config: dict | None = None,
) -> dict:
    """
    Read one GNSS position from gpsd.

    Returns:
        {
            "gps_status": "fix_2d" | "fix_3d"
                          | "no_fix"
                          | "gpsd_no_tpv"
                          | "gpsd_unavailable",
            "latitude": float | None,
            "longitude": float | None,
        }
    """

    cfg = config or load_config()

    gnss = cfg["gnss"]

    host = str(
        gnss["host"]
    )

    port = int(
        gnss["port"]
    )

    timeout_sec = float(
        gnss["timeout_sec"]
    )

    saw_tpv = False

    try:
        with socket.create_connection(
            (host, port),
            timeout=2.0,
        ) as sock:

            sock.settimeout(
                1.0
            )

            sock.sendall(
                b'?WATCH={"enable":true,"json":true};\n'
            )

            deadline = (
                time.monotonic()
                + timeout_sec
            )

            buffer = b""

            while (
                time.monotonic()
                < deadline
            ):

                try:
                    chunk = sock.recv(
                        4096
                    )

                except socket.timeout:
                    continue

                if not chunk:
                    break

                buffer += chunk

                while b"\n" in buffer:

                    line, buffer = (
                        buffer.split(
                            b"\n",
                            1,
                        )
                    )

                    try:
                        msg = json.loads(
                            line.decode(
                                "utf-8",
                                errors="replace",
                            )
                        )

                    except json.JSONDecodeError:
                        continue

                    if (
                        msg.get("class")
                        != "TPV"
                    ):
                        continue

                    saw_tpv = True

                    mode = int(
                        msg.get(
                            "mode",
                            0,
                        )
                        or 0
                    )

                    latitude = (
                        msg.get("lat")
                    )

                    longitude = (
                        msg.get("lon")
                    )

                    if (
                        mode >= 2
                        and isinstance(
                            latitude,
                            (int, float),
                        )
                        and isinstance(
                            longitude,
                            (int, float),
                        )
                    ):

                        return {
                            "gps_status": (
                                "fix_3d"
                                if mode >= 3
                                else "fix_2d"
                            ),

                            "latitude": float(
                                latitude
                            ),

                            "longitude": float(
                                longitude
                            ),
                        }

    except (
        OSError,
        ConnectionError,
    ):

        return {
            "gps_status":
                "gpsd_unavailable",

            "latitude": None,

            "longitude": None,
        }

    if saw_tpv:
        status = "no_fix"
    else:
        status = "gpsd_no_tpv"

    return {
        "gps_status": status,

        "latitude": None,

        "longitude": None,
    }


def main() -> None:

    position = (
        get_gnss_position()
    )

    print(
        "===== RON-V GNSS ====="
    )

    print(
        "status       :",
        position["gps_status"],
    )

    print(
        "has_position :",
        position["latitude"]
        is not None
        and position["longitude"]
        is not None,
    )


if __name__ == "__main__":
    main()
PY

14. GNSSモジュールを確認する

構文確認:

cd /home/ronv/ronv

/home/ronv/.venvs/ronv/bin/python \
  -m py_compile \
  app/gnss/gpsd_reader.py

実行:

/home/ronv/.venvs/ronv/bin/python \
  -m app.gnss.gpsd_reader

今回の実機確認は屋内で行いました。

結果:

===== RON-V GNSS =====
status       : no_fix
has_position : False

これは異常ではありません。

gpsdとの通信はできていますが、屋内のため衛星位置Fixが成立していない状態です。


15. Python仮想環境について

今回のコマンドは、

/home/ronv/.venvs/ronv/bin/python \
  -m app.gnss.gpsd_reader

です。

シェルのプロンプトに、

(ronv)

が表示されていなくても、RON-VのPython仮想環境を使用しています。

確認する場合は、

/home/ronv/.venvs/ronv/bin/python - <<'PY'
import sys

print(
    "executable :",
    sys.executable,
)

print(
    "prefix     :",
    sys.prefix,
)

print(
    "base       :",
    sys.base_prefix,
)

print(
    "venv       :",
    sys.prefix != sys.base_prefix,
)
PY

とします。

RON-Vではsystemdからも同じ絶対パスを使用するため、この方法を標準とします。


16. LTEとGNSSを1回の測定にまとめる

次に、

app/lte/measure_once.py

を作成します。

このプログラムは、

GNSS取得
   ↓
LTE cell_search
   ↓
SQLite保存

を1回だけ実行します。


17. measure_once.py を作成する

cd /home/ronv/ronv

cat > app/lte/measure_once.py <<'PY'
from __future__ import annotations

from app.common.config import load_config
from app.common.db import initialize_database
from app.gnss.gpsd_reader import (
    get_gnss_position,
)
from app.lte.cell_search import (
    run_cell_search,
)
from app.lte.measurement_store import (
    initialize_lte_schema,
    save_lte_measurements,
)


def main() -> None:

    cfg = load_config()

    initialize_database(
        cfg
    )

    initialize_lte_schema(
        cfg
    )

    print(
        "===== RON-V LTE MEASUREMENT ====="
    )

    print(
        "GNSS reading..."
    )

    position = get_gnss_position(
        cfg
    )

    print(
        "gnss_status :",
        position["gps_status"],
    )

    print(
        "gps_position:",
        "yes"
        if (
            position["latitude"]
            is not None
            and position["longitude"]
            is not None
        )
        else "no",
    )

    print()

    print(
        "LTE cell search..."
    )

    cells, raw_log = (
        run_cell_search(
            cfg
        )
    )

    print(
        "cells       :",
        len(cells),
    )

    for cell in cells:

        print(
            "CELL",

            f'pci={cell["pci"]}',

            f'earfcn={cell["earfcn"]}',

            f'freq='
            f'{cell["frequency_mhz"]:.1f}'
            f'MHz',

            f'pss='
            f'{cell["pss_power_dbm"]:.1f}'
            f'dBm',
        )

    ids = save_lte_measurements(
        cells=cells,
        raw_log=raw_log,
        position=position,
        config=cfg,
    )

    print()

    print(
        "saved_rows  :",
        len(ids),
    )

    print(
        "row_ids     :",
        ids,
    )

    print(
        "result      : OK"
    )


if __name__ == "__main__":
    main()
PY

18. 全モジュールの構文確認

cd /home/ronv/ronv

/home/ronv/.venvs/ronv/bin/python \
  -m py_compile \
  app/common/config.py \
  app/common/db.py \
  app/gnss/gpsd_reader.py \
  app/lte/cell_search.py \
  app/lte/measurement_store.py \
  app/lte/measure_once.py

何も表示されなければ正常です。


19. GNSS + LTE測定を1回実行する

cd /home/ronv/ronv

/home/ronv/.venvs/ronv/bin/python \
  -m app.lte.measure_once

今回の実機では、

===== RON-V LTE MEASUREMENT =====

GNSS reading...

gnss_status : no_fix
gps_position: no

LTE cell search...

cells       : 1

CELL
pci=123
earfcn=9510
freq=788.0MHz
pss=-23.3dBm

saved_rows  : 1
row_ids     : [1]

result      : OK

となりました。


20. 実機で取得したLTE情報

今回保存できたデータは、

Band        : 28
Frequency   : 788.0 MHz
EARFCN      : 9510
PCI         : 123
PRB         : 50
ports       : 2
PSS power   : -23.3 dBm
PCI count   : 1
GPS status  : no_fix
status      : cell_found

です。

GPSは屋内だったため、

latitude  = NULL
longitude = NULL

です。


21. SQLite保存結果を確認する

位置情報そのものは表示せず、位置が保存されたかどうかだけを確認します。

sqlite3 /var/lib/ronv/ronv.db <<'SQL'
.headers on
.mode column

SELECT
    id,
    timestamp_jst,
    band,
    frequency_mhz,
    earfcn,
    pci,
    prb,
    ports,
    pss_power_dbm,
    pci_count,
    gps_status,

    CASE
        WHEN latitude IS NOT NULL
         AND longitude IS NOT NULL
        THEN 'yes'
        ELSE 'no'
    END AS gps_position,

    status

FROM lte_measurements

ORDER BY id DESC

LIMIT 10;

PRAGMA integrity_check;
SQL

実機では、

id
1

band
28

frequency_mhz
788.0

earfcn
9510

pci
123

prb
50

ports
2

pss_power_dbm
-23.3

pci_count
1

gps_status
no_fix

gps_position
no

status
cell_found

となりました。

最後に、

integrity_check
---------------
ok

を確認しました。


22. 0セルでもデータを保存する

RON-Vでは、

セルを検出した地点

だけでなく、

セルを検出できなかった地点

も重要です。

そのため save_lte_measurements() は、セルが0件だった場合でも、

status    = no_cells
pci_count = 0
pci       = NULL

として記録します。

これにより走行測定時に、

電波が取れた地点
電波が取れなかった地点

の両方を後から解析できます。


23. 複数PCIの場合

同じEARFCNで複数セルを検出する場合があります。

例えば、

PCI 123
PCI 122

を同時に検出した場合、

lte_measurements

にはPCIごとに1行保存します。

ただし、

timestamp
latitude
longitude
pci_count

は同じ測定結果として共有します。

例えば、

row 1
PCI = 123
pci_count = 2

row 2
PCI = 122
pci_count = 2

となります。


24. PSS powerについて

今回、

PSS power = -23.3 dBm

を取得しました。

ただしRTL-SDR Blog V4は、絶対電力値を保証した測定器ではありません。

RON-Vでは、

同一RTL-SDR
同一アンテナ
同一gain
同一測定条件

で、

地点A
地点B
地点C

を比較するための相対的な受信レベルとして扱います。


25. Grafana Geomapへ向けたデータ構造

今回の lte_measurements には、

latitude
longitude

を用意しています。

GNSS Fixが得られれば、

timestamp_jst
latitude
longitude
pci
pci_count
pss_power_dbm
earfcn

を1レコードとしてGrafanaへ渡せます。

Geomapでは概念的に、

地図上の地点
    │
    ├─ PCI
    ├─ PCI count
    ├─ EARFCN
    └─ PSS power

を表示できます。


26. 現在のGeomap実装

この記事ではSQLiteへGNSS + LTEを保存するところを主題としましたが、現在のGitHubにはGrafana dashboard定義も保存されています。

grafana/dashboards/ronv-mie-map.json

Geomapでは latitude / longitude を使い、LTEやRFの観測結果を地理的に確認できる構成へ発展しています。記事内のデータモデルはこの後段のGeomapでもそのまま利用します。

27. 今回完成したデータ経路

u-blox GNSS
      ↓
    gpsd
      ↓
gpsd_reader.py
      │
      │
      ├─ GPS Fixあり
      │      ↓
      │ latitude / longitude
      │
      └─ GPS Fixなし
             ↓
          no_fix

RTL-SDR Blog V4
      ↓
SoapySDR
      ↓
srsRAN 4G
      ↓
cell_search.py
      │
      ├─ PCI
      ├─ EARFCN
      ├─ PRB
      ├─ ports
      └─ PSS power
             │
             ▼
      measure_once.py
             │
             ▼
      lte_measurements
             │
             ▼
 /var/lib/ronv/ronv.db
             │
             ▼
          Grafana

28. 今回確認できたこと

[OK] LTE measurement SQLite backup

[OK] lte_measurements作成

[OK] timestamp_jst

[OK] LTE Band

[OK] frequency

[OK] EARFCN

[OK] PCI

[OK] PRB

[OK] ports

[OK] PSS power

[OK] PCI count

[OK] latitude column

[OK] longitude column

[OK] gps_status

[OK] raw cell_search log

[OK] INDEX ×3

[OK] SQLite WAL

[OK] integrity_check

[OK] Grafanaからlte_measurements確認

[OK] gpsd_reader.py

[OK] gpsd TCP接続

[OK] 屋内 no_fix処理

[OK] measure_once.py

[OK] GNSS + LTE統合

[OK] 実LTEセル検出

[OK] SQLiteへ実測結果保存

29. 現在のRON-V

ここまでで、

Raspberry Pi 5
│
├─ GNSS
│    └─ gpsd
│
├─ Vehicle
│    └─ AtomS3R
│         ↓
│       SQLite
│
├─ Camera
│    └─ Logitech C270
│
├─ LTE
│    └─ RTL-SDR Blog V4
│         ↓
│       SoapySDR
│         ↓
│       srsRAN
│
├─ SQLite
│    ├─ vehicle_events
│    └─ lte_measurements
│
├─ Controller
│
├─ Web
│    └─ Flask + Gunicorn
│
└─ Grafana
     └─ RON-V SQLite

となりました。


30. 次回

次回はRON-Vへ、

Meshtastic

を追加します。

既存の920MHz Meshtasticノードを、

Meshtastic Node
      ↓
USB
      ↓
Raspberry Pi 5
      ↓
RON-V

として接続します。

RON-VではBluetooth接続ではなく、

USB接続

を基本構成として検討します。

最終的には、

LTE
GNSS
Vehicle
Camera
Meshtastic

を同じ車載観測ノードで扱える構成へ進めます。


まとめ

今回は、RON-Vで取得したGNSS情報とLTEセル情報を組み合わせ、

lte_measurements

としてSQLiteへ保存する仕組みを構築しました。

実機では、

Band 28
EARFCN 9510
788.0 MHz
PCI 123
PSS power -23.3 dBm

を取得し、

row_id = 1

としてSQLiteへ正常に保存できました。

GNSSは屋内試験だったため、

no_fix

でしたが、Fixが得られた場合に、

latitude
longitude

を同じLTE測定レコードへ保存できる構造になっています。

これで、

GNSS
  +
LTE
  ↓
SQLite
  ↓
Grafana Geomap

へ進むためのデータ基盤が完成しました。

次回は、

RON-V #13 — MeshtasticをUSB接続し、920MHz Mesh通信をRON-Vへ取り込む

へ進みます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?