Open-source desktop tool for tree-ring cross-dating, measurement, and master chronology building.
This project has been archived.
The maintainers of this project have marked this project as archived. No new releases are expected.
Project description
Fritts — Dendrochronology Analysis Platform
An open-source desktop application for tree-ring cross-dating, measurement, and master chronology building. Built with Python, PyQt6, and PyQtGraph. Named in honor of Harold C. Fritts (1930–2024), author of Tree Rings and Climate and a pioneer of dendroclimatology.
Repository: GitHub · GitLab
Package: fritts-dendro on PyPI
Table of Contents
- Why Fritts?
- Features
- Quick Start
- Installation
- Supported Formats
- Dependencies
- Usage
- Cross-Dating Methods
- COFECHA-Style Quality Control
- Chronology Building
- Image Measurement
- Session Workspace
- ITRDB Integration
- Keyboard Shortcuts
- Sample Data
- Project Structure
- Development
- User Guide
- Target Users
- Tech Stack
- Known Issues
- License
Why Fritts?
Existing dendrochronology software is often dated, proprietary, Windows-only, or splits critical workflows across multiple applications. Fritts unifies format parsing, interactive visual plotting, statistical cross-dating, and chronology building into a single, modern, cross-platform interface.
Key differentiators:
- All-in-one — Import, visualize, cross-date, detrend, build chronologies, and export in a single application.
- Modern GUI — PyQtGraph-powered canvas with GPU-accelerated rendering, smooth zoom/pan, and multi-series overlay.
- Standards-compliant — Full Tucson decadal read/write, TRiDaS read/write, Heidelberg read.
- COFECHA-compatible QC — Sliding-window correlation with AR prewhitening and Spearman significance (matching
dplRdefaults). - ITRDB integration — Search and download data directly from the NOAA International Tree-Ring Data Bank.
- Cross-platform — Linux, macOS, and Windows with optional standalone binaries.
Features
- Multi-format import/export — Tucson (.rwl, .tuc, .crn), Heidelberg (.fh), TRiDaS (.xml).
- Interactive plotting — PyQtGraph canvas with smooth zoom, pan, multi-series overlay, and skeleton plot mode.
- Statistical cross-dating — Baillie-Pilcher t-value, Hollstein t-value, Gleichläufigkeit (GLK) with Buras-Wilmking 2015 correction, and sliding-window analysis.
- COFECHA-style Quality Control — Leave-one-out master chronology, Yule-Walker AR(p) prewhitening (AIC order selection), segment correlation with Spearman's rho and p-value thresholding. Adjustable alpha via the QC dialog.
- Detrending — Mean, negative exponential, Hugershoff, cubic spline, and Regional Curve Standardisation (RCS).
- Chronology builder — Interactive master chronology with real-time EPS and R-bar metrics. Biweight robust mean averaging.
- Classical ring detection — Projection-profile boundary detection on scanned wood-section images.
- Geometric pith estimator — Adjustable concentric-circle overlay for estimating missing distance to pith.
- Session workspace — Save and load complete project state to
.frittsJSON files (all series, references, metadata). - ITRDB data search — Browse and download from the NOAA International Tree-Ring Data Bank.
- R/dplR export — Generate companion
.Rscripts for advanced analysis in R'sdplRpackage. - Preferences — DPI and theme settings persisted via
QSettings. - Full undo/redo — Command-pattern architecture with 200-step undo history.
- Sample data — Two synthetic 100-year series in
sample_data/example.rwlfor evaluation.
Quick Start
# Install from PyPI
pip install fritts-dendro
# Launch
fritts
Or from source:
git clone https://github.com/mabo-du/fritts.git
cd fritts
pip install -e ".[dev]"
fritts
First-run workflow
- Import data:
File > Import(Ctrl+Shift+I) — select a.rwl,.fh, or.xmlfile. - Explore: Click series in the Series List to view statistics. Toggle visibility with checkboxes.
- Cross-date: Right-click a floating series → "Cross-Date Series", or
Tools > Cross-Date(Ctrl+D). - Detrend:
Tools > Detrend— remove biological growth trends before chronology building. - Build chronology:
Tools > Build Chronology(Ctrl+B) — create a master curve from reference series. - QC:
Tools > QC Report— run COFECHA-style quality control with adjustable alpha threshold. - Save:
File > Save Workspace(Ctrl+S) — save your session as a.frittsproject file. - Export:
File > Export(Ctrl+E) — export as Tucson.rwl, TRiDaS.xml, or R/dplR script.
Installation
From PyPI (recommended)
pip install fritts-dendro
fritts
From source (development)
git clone https://github.com/mabo-du/fritts.git
cd fritts
# Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # Linux/macOS
# .venv\Scripts\activate # Windows
# Install with dev dependencies
pip install -e ".[dev]"
# Launch
fritts
Standalone binaries
Pre-built executables are available from the GitHub Releases page:
| Platform | File | Notes |
|---|---|---|
| Linux | fritts-linux-x64 + Fritts-x86_64.AppImage |
AppImage is portable |
| macOS | fritts-macos-arm64 |
Apple Silicon (M1+) |
| Windows | fritts-windows-x64.exe |
Standalone executable |
System dependencies
Ubuntu/Debian:
sudo apt install libxcb-cursor0 libegl1 libgl1
Fedora:
sudo dnf install qt6-qtbase-gui
Supported Formats
| Format | Extensions | Read | Write | Description |
|---|---|---|---|---|
| Tucson Decadal | .rwl, .tuc, .crn |
✅ | ✅ | Most common tree-ring format. 0.01mm or 0.001mm precision, auto-detected from stop code (999 or -9999). |
| Heidelberg | .fh |
✅ | — | German format with HEADER/DATA sections. Auto-detects single-column and decadal-block layouts. |
| TRiDaS XML | .xml |
✅ | ✅ | International standard (TRiDaS 1.2.2). Handles astronomical BCE convention. |
| Fritts Workspace | .fritts |
✅ | ✅ | JSON project file preserving all series, references, metadata, and format version. |
Precision handling
- Tucson: Stop code
999→ 0.01 mm precision (divide by 100). Stop code-9999→ 0.001 mm precision (divide by 1000). Mixed stop codes warn and use the dominant value as fallback. - TRiDaS: Unit attribute determines precision (
1/100th millimetres→ /100,micrometre→ /1000,millimetre→ /1). - Heidelberg: Values assumed in 1/100 mm (divide by 100).
Dependencies
| Package | Minimum | Purpose |
|---|---|---|
| PyQt6 | 6.6 | GUI framework |
| pyqtgraph | 0.13 | GPU-accelerated interactive plotting |
| pandas | 2.1 | Data manipulation and alignment |
| numpy | 1.26 | Numerical computation |
| scipy | 1.12 | Statistical algorithms (Pearson, Spearman, splines) |
| lxml | 5.0 | TRiDaS XML parsing |
| tifffile | 2024.1 | Memory-mapped TIFF loading for large images |
All dependencies have explicit upper bounds in pyproject.toml to prevent breaking on future major releases.
Usage
Launching
fritts
Or directly:
python -m dendro.main
The Interface
The main window is divided into three panels:
| Panel | Location | Content |
|---|---|---|
| Series List | Left | All loaded series with checkboxes, year ranges, visibility toggles. Right-click for context menu (Cross-Date, Set as Reference, Remove). |
| Plot Area | Centre | PyQtGraph canvas showing ring-width curves. Multi-series overlay with zoom/pan. |
| Stats Panel | Right | Selected series statistics: mean, standard deviation, min/max, year range, ring count. After cross-dating: t-values, GLK, overlap. |
Series List operations
| Action | How |
|---|---|
| Select a series | Left-click → shows stats in right panel |
| Toggle visibility | Checkbox |
| Cross-date | Right-click → "Cross-Date Series" |
| Set as reference | Right-click → "Set as Reference" |
| Remove | Right-click → "Remove Series" (undoable) |
| Shift in time | Arrow keys (← →) with active selection. Clamped to ±10,000 years. |
| Snap to offset | Click a proposed start year in the cross-date results panel |
Plot controls
| Control | Action |
|---|---|
| Left-click + drag | Pan |
| Scroll wheel | Zoom in/out |
| Ctrl + scroll | Zoom X-axis only |
| Shift + scroll | Zoom Y-axis only |
| Right-click | Context menu |
| F key | Zoom to fit |
Cross-Dating Methods
Fritts provides three complementary statistics for cross-dating:
Baillie-Pilcher t-value (t_bp)
A 5-year running mean is applied to both series before computing Pearson's r and converting to a t-statistic: t = r · √((n−6)/(1−r²)). This pre-smoothing reduces the influence of high-frequency noise and emphasises decadal-scale patterns.
- Degrees of freedom: n−6 (4 lost to the running mean, 2 to bivariate correlation).
- Reference: Baillie & Pilcher (1973), Tree-Ring Bulletin 33:7–14.
- Typical significance: t > 3.5 for 50-year overlap.
Hollstein t-value (t_ho)
Each series is transformed to Wuchswerte (growth-change values) before correlation: Wᵢ = 100 · ln(xᵢ / xᵢ₋₁). This year-over-year ratio transform emphasises short-term growth changes. The canonical formula matches dendroNetwork::wuchswerte() (Hollstein 1980, pp 14–15).
- Degrees of freedom: n−3 (1 lost to the lag, 2 to bivariate correlation).
- Reference: Hollstein (1980), Mitteleuropäische Eichenchronologie.
Gleichläufigkeit (GLK)
GLK measures the percentage of years where both series show the same direction of change (up/up or down/down). Implemented with the Buras-Wilmking (2015) correction:
- Year-pairs where both series show zero change count as a synchronous match.
- Year-pairs where only one series shows zero change are excluded.
- Z-score and p-value are computed using the exact binomial test (n < 30) or normal approximation (n ≥ 30).
- Reference: Buras & Wilmking (2015), Dendrochronologia 33:42–48.
Interpreting results
| Statistic | Threshold | Meaning |
|---|---|---|
| t_bp / t_ho | > 3.5 | Strong match, series likely correctly dated |
| t_bp / t_ho | 2.5–3.5 | Possible match, investigate further |
| t_bp / t_ho | < 2.5 | Weak match |
| GLK | > 60% | Good trend agreement |
| GLK | > 70% | Very strong trend agreement |
| p-value | < 0.05 | Statistically significant |
Sliding window cross-dating
Tools > Cross-Date (Ctrl+D) runs crossdate_sliding() which slides the sample across the reference at every position where the overlap exceeds the minimum threshold (default 30 years). At each position, all three statistics (t_bp, t_ho, GLK) are computed on the overlapping segment. Results are displayed as a table ranked by t_bp.
COFECHA-Style Quality Control
Tools > QC Report runs a COFECHA-style analysis following dplR::corr.series.seg() conventions:
- Leave-one-out master: For each target series, a master chronology is built from the mean (or biweight robust mean) of all other series.
- AR prewhitening: Each full-length series is prewhitened via Yule-Walker AR(p) with AIC order selection (max order 5), removing autocorrelation that would inflate apparent significance. Matching dplR's
normalize.xdate()behaviour. - Sliding windows: Fixed-length segments (default 50 years, 25-year overlap) are correlated against the master.
- Spearman's rho with p-value: Segments are flagged when
p > α(default α = 0.05). This adapts to varying segment lengths and df loss from prewhitening — unlike COFECHA's hardcoded r < 0.32 threshold which only applies to n=50. - Adjustable alpha: The QC dialog provides an alpha spinbox (0.001–1.0) with a "Run QC" button to re-run at different thresholds.
Chronology Building
Tools > Build Chronology (Ctrl+B) creates a master chronology from selected reference series:
- Select reference series.
- Choose averaging method: Mean or Biweight robust mean (resistant to outliers, recommended).
- The chronology is built with EPS and R-bar calculated in real time:
- R-bar: Mean inter-series correlation across all pairwise overlaps (Pearson r).
- EPS (Expressed Population Signal):
EPS = (n · r̄) / (1 + (n−1) · r̄). Threshold for a well-represented chronology: ≥ 0.85.
- The chronology appears in the Series List as
CHRONOLOGY. - Right-click → "Cross-Date Series" to date floating series against the master chronology.
Image Measurement
Tools > Image Measurement opens the wood-section image viewer:
- Load an image — JPEG, PNG, or TIFF. Files >500 MB use memory-mapped tifffile I/O for efficient loading.
- Set DPI — Calibrate the image resolution for accurate millimeter measurements.
- Detect rings — Click "Auto-Detect Rings" to run the classical projection-profile boundary detection algorithm.
- Manual markers — Left-click to place/adjust ring boundary markers.
- Extract series — Click "Extract Series" with ≥2 markers to export ring-width measurements. Series IDs are validated (≤8 alphanumeric characters).
- Pith estimator — Toggle concentric-circle overlay to estimate missing distance to pith.
The channel order for loaded images is RGB (matching pyqtgraph's makeARGB expectations). Grayscale images are automatically broadcast to 3-channel.
Session Workspace
File > Save Workspace (Ctrl+S) serialises the entire session (all series, detrended indices, reference flags, metadata) to a .fritts JSON file:
- NaN values → JSON
nullfor standard compliance. format_versionfield for forward/backward compatibility.File > Open Workspace(Ctrl+O) to reload.File > Save As(Ctrl+Shift+S) for versioned saves.
ITRDB Integration
Tools > Search ITRDB connects to the NOAA International Tree-Ring Data Bank API:
- Search: Keyword search across all published ITRDB studies (default limit 30 results to avoid API errors).
- Download: Select a study and click "Download Selected" to fetch available
.rwlseries. - Security: SSRF guard (host whitelisted to
ncei.noaa.gov), SSL context, 100 MB Content-Length cap. - User-Agent:
Fritts/{version}for API identification.
API endpoint: https://www.ncei.noaa.gov/access/paleo-search/study/search.json
Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Ctrl+O |
Open Workspace |
Ctrl+S |
Save Workspace |
Ctrl+Shift+S |
Save Workspace As |
Ctrl+Shift+I |
Import data |
Ctrl+E |
Export data |
Ctrl+, |
Preferences |
Ctrl+Z |
Undo |
Ctrl+Shift+Z / Ctrl+Y |
Redo |
Ctrl+D |
Cross-Date |
Ctrl+B |
Build Chronology |
F |
Zoom to Fit |
← / → |
Shift active series ±1 year |
Ctrl+Q |
Quit |
Sample Data
The repository includes sample_data/example.rwl — two synthetic 100-year series (SAMPLEA, SAMPLEB) generated from an age-related exponential trend with AR(1) noise. These are suitable for:
- Testing import/export workflows
- Evaluating cross-dating statistics
- Practicing chronology building
- Familiarisation with the interface
fritts
# File > Import → select sample_data/example.rwl
Project Structure
fritts/
├── assets/ # Static assets (screenshots, icons)
├── docs/
│ ├── USER_GUIDE.md # Comprehensive user documentation
│ ├── scope.md # Project scope and roadmap
│ └── research-papers/ # Reference papers
├── sample_data/ # Synthetic ring-width files
│ └── example.rwl # 2 series, 100 years each
├── src/
│ └── dendro/
│ ├── io/ # Format parsers
│ │ ├── tucson.py # Tucson decadal (.rwl) read/write
│ │ ├── heidelberg.py# Heidelberg (.fh) reader
│ │ ├── tridas.py # TRiDaS XML read/write
│ │ ├── itrdb.py # NOAA ITRDB API client
│ │ └── export_r.py # dplR-compatible R script generator
│ ├── models/
│ │ ├── series.py # RingWidthSeries (frozen dataclass)
│ │ ├── session.py # SessionManager with serialization
│ │ └── commands.py # Command pattern (undo/redo)
│ ├── stats/
│ │ ├── crossdate.py # t_bp, t_ho, GLK, sliding cross-date
│ │ ├── chronology.py # Chronology builder, rbar, EPS
│ │ ├── detrend.py # Mean, neg exp, spline, RCS
│ │ ├── quality_control.py # COFECHA-style QC
│ │ └── ai_segmentation.py # Classical ring detection
│ ├── ui/
│ │ ├── main_window.py # Application window
│ │ ├── series_list.py # Series list panel
│ │ ├── series_view.py # Plot panel
│ │ ├── image_view.py # Image measurement
│ │ ├── chronology_builder.py # Chronology dialog
│ │ ├── preferences_dialog.py # Settings dialog
│ │ └── qc_dialog.py # QC report dialog
│ └── main.py # Application entry point
├── tests/ # Test suite (pytest)
│ ├── test_regression.py # 35+ regression tests
│ ├── test_crossdate.py # Cross-dating edge cases
│ ├── test_session_serialization.py # Save/load round-trip
│ ├── test_io_formats.py # Heidelberg, TRiDaS parsers
│ ├── test_ui_smoke.py # UI smoke tests (pytest-qt)
│ └── ...
├── pyproject.toml # Build configuration
└── README.md
Development
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests (95+ tests)
pytest
# Run linting
ruff check src/ tests/
# Format code
ruff format src/ tests/
Test organization
| File | Coverage |
|---|---|
test_regression.py |
FR-specific regression tests (statistics, models, signals) |
test_session_serialization.py |
.fritts save/load round-trip, version validation |
test_commands.py |
Command pattern (DetrendCommand, undo/redo) |
test_crossdate.py |
Cross-dating edge cases (no overlap, min_overlap, identical series) |
test_tucson_parser.py |
Tucson read/write round-trip (BCE, non-decade starts) |
test_io_formats.py |
Heidelberg and TRiDaS parser tests |
test_detrend.py |
Detrending methods (mean, neg exp, spline) |
test_itrdb.py |
ITRDB API client (mocked HTTP) |
test_ui_smoke.py |
UI instantiation smoke tests (pytest-qt) |
Contributing
- Fork the repository on GitHub or GitLab.
- Create a feature branch from
master. - Make your changes with tests.
- Run the full test suite:
pytest. - Submit a merge request / pull request.
User Guide
See docs/USER_GUIDE.md for comprehensive documentation covering:
- Installation troubleshooting
- Importing and exporting data
- Cross-dating workflows
- Detrending methods
- Chronology building with EPS/R-bar interpretation
- COFECHA-style Quality Control (prewhitening, alpha threshold)
- ITRDB data search
- Image measurement and ring detection
- Keyboard shortcuts reference
- Troubleshooting common issues
Target Users
- Dendrochronologists dating archaeological timbers and historical structures.
- Climate researchers building proxy records from tree rings.
- Wood specialists in archaeology, heritage, and conservation.
- Students learning tree-ring analysis methods.
Tech Stack
| Component | Technology |
|---|---|
| GUI | PyQt6 (Qt 6.6+) |
| Visualization | PyQtGraph (0.13.x, GPU-accelerated) |
| Data | Pandas, NumPy |
| Statistics | SciPy (Pearson r, Spearman rho, splines) |
| XML | lxml (TRiDaS parsing) |
| Image I/O | tifffile (large TIFF memory-mapped loading) |
| Testing | pytest, pytest-qt, pytest-mock |
| Linting | ruff |
| Build | setuptools, PyInstaller, AppImage |
Known Issues
NOAA ITRDB API limit
The ITRDB search API returns HTTP 500 errors when the limit parameter is 46 or higher. Fritts defaults to limit=30 to avoid this. If you need more results, run multiple searches with different keywords.
pyqtgraph version
Fritts requires pyqtgraph>=0.13,<0.14. Version 0.14 removed a method that pyqtgraph's own internal plotting code still calls, causing AttributeError spam on plot interactions. This is pinned in pyproject.toml. A warning is shown at startup if >=0.14 is detected.
TRiDaS NaN handling
The TRiDaS writer converts NaN ring-width values to 0. This is a known limitation of the current export implementation.
Large image loading
For TIFF files >500 MB, the QImage path has a 2× memory overhead (RGBA decode + numpy copy). Fritts automatically switches to tifffile memory-mapped I/O for files above this threshold, avoiding the double allocation. For extremely large files (>2 GB), ensure sufficient system memory for pyqtgraph's texture upload.
Undo stack
The undo stack is limited to 200 commands. When exceeded, the oldest commands are discarded. This prevents unbounded memory growth during long sessions.
License
MIT
Fritts — Named in honor of Harold C. Fritts (1930–2024), pioneer of dendroclimatology.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fritts_dendro-0.2.0.tar.gz.
File metadata
- Download URL: fritts_dendro-0.2.0.tar.gz
- Upload date:
- Size: 95.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d525e24e5a00a529456dc0a64c6c0fce58fb31df5bc406ffecf8446e7cc92a0d
|
|
| MD5 |
c4e9a053e76b9a007c4c5906dca08dab
|
|
| BLAKE2b-256 |
c937c847ecfeed4fcc5d12805942c3b57a8eda6d4cfcdaf1b7fdd361ec446b09
|
Provenance
The following attestation bundles were made for fritts_dendro-0.2.0.tar.gz:
Publisher:
release.yml on mabo-du/fritts
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fritts_dendro-0.2.0.tar.gz -
Subject digest:
d525e24e5a00a529456dc0a64c6c0fce58fb31df5bc406ffecf8446e7cc92a0d - Sigstore transparency entry: 1901964939
- Sigstore integration time:
-
Permalink:
mabo-du/fritts@847107d1d67b61574b71fe450f24eee04a654c89 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mabo-du
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@847107d1d67b61574b71fe450f24eee04a654c89 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fritts_dendro-0.2.0-py3-none-any.whl.
File metadata
- Download URL: fritts_dendro-0.2.0-py3-none-any.whl
- Upload date:
- Size: 85.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a0f6c222fd7cd6edff7ec5a5196a93ac057e8416132adea65768860b1950b9a
|
|
| MD5 |
dda03ef7d233810443c940e40651121b
|
|
| BLAKE2b-256 |
3374f59a398d29642b2f7227853ba2b675df118912d49ec484e7f8ddb867c857
|
Provenance
The following attestation bundles were made for fritts_dendro-0.2.0-py3-none-any.whl:
Publisher:
release.yml on mabo-du/fritts
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fritts_dendro-0.2.0-py3-none-any.whl -
Subject digest:
1a0f6c222fd7cd6edff7ec5a5196a93ac057e8416132adea65768860b1950b9a - Sigstore transparency entry: 1901965026
- Sigstore integration time:
-
Permalink:
mabo-du/fritts@847107d1d67b61574b71fe450f24eee04a654c89 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mabo-du
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@847107d1d67b61574b71fe450f24eee04a654c89 -
Trigger Event:
push
-
Statement type: