Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ded7519
added repository URL to pyproject.toml
eureka-0 Nov 26, 2025
6772e61
improvement: renamed workflow file for release process
eureka-0 Nov 26, 2025
06d7397
fix: deleted extra period
eureka-0 Nov 26, 2025
be07bee
added CHANGELOG.md to track changes
eureka-0 Nov 26, 2025
5f3c7e1
added github release action
eureka-0 Nov 26, 2025
94010d8
added release link
eureka-0 Nov 26, 2025
d95535e
added bias data files
eureka-0 Nov 26, 2025
2f3f5f8
added pyarrow dependency and updated requires-python to >=3.10
eureka-0 Nov 26, 2025
cd6235f
improvement: Use environment variables for Python versions
eureka-0 Nov 26, 2025
3d319bf
test: add GitHub Actions workflow for testing changelog extraction
eureka-0 Nov 26, 2025
9e3db73
change: trigger test on push to branch
eureka-0 Nov 26, 2025
aea3e1c
fix: multiline strings output bug
eureka-0 Nov 26, 2025
020be39
fix: backticks issues
eureka-0 Nov 26, 2025
b610d10
fix: echo does not output correctly
eureka-0 Nov 26, 2025
8892e38
chore: added release notes extraction in workflow
eureka-0 Nov 26, 2025
4212746
chore: add rinex v2 data and bias data
eureka-0 Nov 26, 2025
5e745cb
fix: a docstring inconsistency
eureka-0 Nov 26, 2025
293dd29
change: lazy parameter default to False
eureka-0 Nov 26, 2025
bfc290a
feat(tec): add DCB bias file reading support
eureka-0 Nov 26, 2025
3639fac
refactor: code improvements
eureka-0 Nov 26, 2025
e2a122f
refactor(CI): run tests on multiple OSes and python versions
eureka-0 Nov 26, 2025
cd6e152
refactor: improve bias reading flow
eureka-0 Nov 27, 2025
d8d81c9
refactor: for better code clarity and future compatibility
eureka-0 Nov 27, 2025
afd8d03
feat(rinex): add pivot option for future compatibility
eureka-0 Nov 27, 2025
38f1004
refactor(rinex): use a single observable_filter closure for filtering…
eureka-0 Nov 27, 2025
a9a5183
chore(tec): add more codes
eureka-0 Nov 27, 2025
76f8743
chore(rinex): minor improvements
eureka-0 Nov 27, 2025
3f63ac9
feat(tec): apply DCB corrections and improve TEC calculation
eureka-0 Nov 27, 2025
1309eb2
perf(tec): reduce memory usage while resolving observations
eureka-0 Nov 27, 2025
831611f
chore: minor updates
eureka-0 Nov 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 0 additions & 24 deletions .github/workflows/pytest.yml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,67 @@
name: Build and publish to PyPI
name: Release and Publish

on:
push:
tags:
# Trigger on version tags like v1.0.0
# Trigger on version tags like v1.2.3
- v*

env:
PYTHON_VERSION: "3.13"
FREE_THREADED_PYTHON_VERSION: "3.13t"

jobs:
release:
name: Create Github release
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Extract Unreleased section from CHANGELOG.md
id: extract_changelog
shell: bash
run: |
body=$(awk '
/^## \[Unreleased\]/ { flag=1; next }
/^## \[/ && flag { exit }

flag {
lines[++n] = $0
}

END {
if (n == 0) exit 0

start = 1
while (start <= n && lines[start] ~ /^[[:space:]]*$/) start++

end = n
while (end >= start && lines[end] ~ /^[[:space:]]*$/) end--

if (end < start) exit 0

for (i = start; i <= end; i++) {
print lines[i]
}
}' CHANGELOG.md)

echo 'body<<EOF' >> $GITHUB_OUTPUT
printf '%s\n' "$body" >> $GITHUB_OUTPUT
echo 'EOF' >> $GITHUB_OUTPUT

- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
body: ${{ steps.extract_changelog.outputs.body }}
prerelease: false
draft: true

linux:
name: Build wheels for Linux
runs-on: ubuntu-latest

strategy:
Expand All @@ -22,14 +76,14 @@ jobs:
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist -i python3.13
args: --release --out dist -i python${{ env.PYTHON_VERSION }}
manylinux: manylinux_2_28

- name: Build free-threaded wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist -i python3.13t
args: --release --out dist -i python${{ env.FREE_THREADED_PYTHON_VERSION }}
manylinux: manylinux_2_28

- name: Upload wheels
Expand All @@ -39,6 +93,7 @@ jobs:
path: dist

musllinux:
name: Build wheels for Musl Linux
runs-on: ubuntu-latest

strategy:
Expand All @@ -53,14 +108,14 @@ jobs:
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist -i python3.13
args: --release --out dist -i python${{ env.PYTHON_VERSION }}
manylinux: musllinux_1_2

- name: Build free-threaded wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist -i python3.13t
args: --release --out dist -i python${{ env.FREE_THREADED_PYTHON_VERSION }}
manylinux: musllinux_1_2

- name: Upload wheels
Expand All @@ -70,6 +125,7 @@ jobs:
path: dist

windows:
name: Build wheels for Windows
runs-on: windows-latest

strategy:
Expand All @@ -80,10 +136,10 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6

- name: Setup Python 3.13
- name: Setup Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v6
with:
python-version: "3.13"
python-version: ${{ env.PYTHON_VERSION }}
architecture: ${{ matrix.target }}

- name: Build wheels
Expand All @@ -92,17 +148,17 @@ jobs:
target: ${{ matrix.target }}
args: --release --out dist

- name: Setup Python 3.13t
- name: Setup Python ${{ env.FREE_THREADED_PYTHON_VERSION }}
uses: actions/setup-python@v6
with:
python-version: "3.13t"
python-version: ${{ env.FREE_THREADED_PYTHON_VERSION }}
architecture: ${{ matrix.target }}

- name: Build free-threaded wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist -i python3.13t
args: --release --out dist -i python${{ env.FREE_THREADED_PYTHON_VERSION }}

- name: Upload wheels
uses: actions/upload-artifact@v5
Expand All @@ -111,6 +167,7 @@ jobs:
path: dist

macos:
name: Build wheels for macOS
runs-on: macos-latest

strategy:
Expand All @@ -121,27 +178,27 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6

- name: Setup Python 3.13
- name: Setup Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v6
with:
python-version: "3.13"
python-version: ${{ env.PYTHON_VERSION }}

- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist -i python3.13
args: --release --out dist -i python${{ env.PYTHON_VERSION }}

- name: Setup Python 3.13t
- name: Setup Python ${{ env.FREE_THREADED_PYTHON_VERSION }}
uses: actions/setup-python@v6
with:
python-version: "3.13t"
python-version: ${{ env.FREE_THREADED_PYTHON_VERSION }}

- name: Build free-threaded wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist -i python3.13t
args: --release --out dist -i python${{ env.FREE_THREADED_PYTHON_VERSION }}

- name: Upload wheels
uses: actions/upload-artifact@v5
Expand All @@ -150,6 +207,7 @@ jobs:
path: dist

sdist:
name: Build sdist
runs-on: ubuntu-latest

steps:
Expand All @@ -168,8 +226,8 @@ jobs:
name: wheels-sdist
path: dist

release:
name: Release
publish:
name: Collect artifacts and publish to PyPI
runs-on: ubuntu-latest
needs: [linux, musllinux, windows, macos, sdist]
environment: pypi
Expand Down
36 changes: 36 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: CI - Pytest

on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch: {}

jobs:
pytest:
name: Run tests on ${{ matrix.os }} with Python ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}

strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v7
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: uv sync

- name: Check using ruff
run: uv run ruff check .

- name: Run tests
run: uv run pytest
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- `calc_tec` function to calculate TEC from RINEX observation and navigation files
- Support using single layer model (SLM) to map slant TEC to vertical TEC
- Support DCB bias correction using external bias files

## [0.1.0](https://github.com/Eureka-0/pygnss-tec/releases/tag/v0.1.0) - 2025-11-24

### Added

- Initial release of pygnss-tec
- `read_rinex_obs` function that supports reading observation RINEX files and automatically calculating azimuth and elevation angles when navigation RINEX files are provided
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 Xinlong Liu.
Copyright (c) 2025 Xinlong Liu

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# PyGNSS-TEC

[![PyPI - Version](https://img.shields.io/pypi/v/pygnss-tec)](https://pypi.org/project/pygnss-tec/)
![Supported Python Versions](https://img.shields.io/badge/python-%3E%3D3.9-blue)
![Supported Python Versions](https://img.shields.io/badge/python-%3E%3D3.10-blue)
![License](https://img.shields.io/badge/license-MIT-blue)
[![Pytest](https://github.com/Eureka-0/pygnss-tec/actions/workflows/pytest.yml/badge.svg)](https://github.com/Eureka-0/pygnss-tec/actions/workflows/pytest.yml)
[![Test](https://github.com/Eureka-0/pygnss-tec/actions/workflows/test.yml/badge.svg)](https://github.com/Eureka-0/pygnss-tec/actions/workflows/test.yml)

PyGNSS-TEC is a high-performance Python package leveraging Rust acceleration, designed for processing and analyzing Total Electron Content (TEC) data derived from Global Navigation Satellite System (GNSS) observations. The package provides tools for RINEX file reading, TEC calculation, and DCB correction to support ionospheric studies.

Expand Down
Binary file not shown.
Binary file not shown.
Binary file added data/rinex_nav_v2/brdc0100.24g.gz
Binary file not shown.
Binary file added data/rinex_nav_v2/brdc0100.24n.gz
Binary file not shown.
Binary file added data/rinex_obs_v2/bele0100.24d.gz
Binary file not shown.
Binary file not shown.
20 changes: 16 additions & 4 deletions gnss_tec/rinex/read_rinex.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def _handle_fn(fn: str | Path | Iterable[str | Path]) -> list[str]:
fn_list = [str(f) for f in fn]
else:
raise TypeError(
f"The file path must be a str, Path, or an iterable of str/Path, not {fn}."
f"The file path must be a str, Path, or Iterable of str/Path, not {fn}."
)

for f in fn_list:
Expand Down Expand Up @@ -94,6 +94,7 @@ def read_rinex_obs(
t_lim: tuple[str | None, str | None] | list[str | None] | None = None,
codes: Iterable[str] | None = None,
*,
pivot: bool = True,
lazy: Literal[True],
) -> tuple[RinexObsHeader, pl.LazyFrame]: ...

Expand All @@ -106,6 +107,7 @@ def read_rinex_obs(
t_lim: tuple[str | None, str | None] | list[str | None] | None = None,
codes: Iterable[str] | None = None,
*,
pivot: bool = True,
lazy: Literal[False] = False,
) -> tuple[RinexObsHeader, pl.DataFrame]: ...

Expand All @@ -117,6 +119,7 @@ def read_rinex_obs(
t_lim: tuple[str | None, str | None] | list[str | None] | None = None,
codes: Iterable[str] | None = None,
*,
pivot: bool = True,
lazy: bool = False,
) -> tuple[RinexObsHeader, pl.DataFrame | pl.LazyFrame]:
"""Read RINEX observation file into a Polars DataFrame.
Expand All @@ -142,7 +145,12 @@ def read_rinex_obs(
codes (Iterable[str] | None, optional): Specific observation codes to extract
(e.g., ['C1C', 'L1C']). If None, all available observation types are
included. Defaults to None.
lazy (bool, optional): Whether to return a `polars.LazyFrame`. Defaults to True.
pivot (bool, optional): Whether to pivot the DataFrame so that each observation
type has its own column. If False, the DataFrame will be in long format with
'Code' and 'Value' columns. Pivoted format is generally more convenient for
analysis and has better performance. Defaults to True.
lazy (bool, optional): Whether to return a `polars.LazyFrame`. Defaults to
False.

Returns:
(RinexObsHeader, pl.DataFrame | pl.LazyFrame): A Dataclass containing metadata from the RINEX observation file header and a DataFrame or LazyFrame containing the RINEX observation data with following columns.
Expand Down Expand Up @@ -184,7 +192,8 @@ def read_rinex_obs(
nav_fn=nav_fn_list,
constellations=constellations,
t_lim=tuple(t_lim),
codes=codes if codes is None else list(set(codes)),
codes=None if codes is None else list(set(codes)),
pivot=pivot,
)
codes = list(filter(lambda x: re.match(r"[A-Z]\d{1}[A-Z]$", x), result.keys()))
ordered_cols = ["Time", "Station", "PRN"]
Expand All @@ -200,7 +209,10 @@ def read_rinex_obs(
result["Azimuth"], result["Elevation"], _ = pm.ecef2aer(
nav_x, nav_y, nav_z, rx_lat, rx_lon, rx_alt, deg=True
)
ordered_cols += sorted(codes)
if pivot:
ordered_cols += sorted(codes)
else:
ordered_cols += ["Code", "Value"]

header = RinexObsHeader(
version=result.pop("Version"),
Expand Down
Loading
Loading