grdb 0.9.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
grdb-0.9.1/PKG-INFO ADDED
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: grdb
3
+ Version: 0.9.1
4
+ Summary: README.md
5
+ Author-email: Bjørn Mølvig <bjoernmoelvig@gmail.com>, Mads Ehrhorn <mads.ehrhorn@gmail.com>
6
+ Requires-Python: <=3.13,>=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: sqlmodel>=0.0.24
9
+ Requires-Dist: pydantic>=2.11.3
10
+ Provides-Extra: build
11
+ Requires-Dist: build>=1.2.1; extra == "build"
12
+ Requires-Dist: twine>=5.1.1; extra == "build"
13
+ Requires-Dist: bumpver>=2024.1130; extra == "build"
14
+ Provides-Extra: dev
15
+ Requires-Dist: ruff>=0.11.6; extra == "dev"
16
+ Requires-Dist: pytest>=7.0.1; extra == "dev"
17
+ Requires-Dist: ty>=0.0.9; extra == "dev"
18
+ Requires-Dist: bumpver>=2024.1130; extra == "dev"
19
+ Requires-Dist: pytest-cov>=6.1.1; extra == "dev"
20
+
21
+ # grdb
22
+
23
+ A Python library for creating and managing SQLite databases of THz pulse measurement data.
24
+
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ pip install grdb
30
+ ```
31
+
32
+
33
+ ## Examples
34
+
35
+ ### Loading Pulses
36
+
37
+ **Load sample pulses:**
38
+ ```python
39
+ from pathlib import Path
40
+ from grdb import load_pulses
41
+
42
+ db_path = Path("raster.grf")
43
+
44
+ # Load first 100 sample pulses
45
+ samples = load_pulses(db_path, offset=0, limit=100, variant="sample")
46
+ ```
47
+
48
+ **Load reference pulses:**
49
+ ```python
50
+ from pathlib import Path
51
+ from grdb import load_pulses
52
+
53
+ db_path = Path("raster.grf")
54
+
55
+ # Load first 50 reference pulses
56
+ references = load_pulses(db_path, offset=0, limit=50, variant="reference")
57
+ ```
58
+
59
+ **Load all pulses (both samples and references):**
60
+ ```python
61
+ from pathlib import Path
62
+ from grdb import load_pulses
63
+
64
+ db_path = Path("raster.grf")
65
+
66
+ # Load all pulse types (variant=None)
67
+ all_pulses = load_pulses(db_path, offset=0, limit=1000, variant=None)
68
+ ```
69
+
70
+ ### Loading Metadata
71
+
72
+ ```python
73
+ from pathlib import Path
74
+ from grdb import load_metadata
75
+
76
+ db_path = Path("raster.grf")
77
+
78
+ raster_config, device_metadata, raster_metadata, n_refs, n_samples = load_metadata(db_path)
79
+ ```
80
+
81
+ - `raster_config`: Acquisition settings such as raster `patterns`, scan `stepsize`, optional `reference_point`, how often to `acquire_ref_every`, and any multi-pass `repetitions_config`.
82
+ - `device_metadata`: Instrument identifiers including the serial number and firmware version captured at acquisition time.
83
+ - `raster_metadata`: Session-level context like the GRDB app version, optional `raster_id`, acquisition `timestamp`, user-supplied `annotations`, raw `device_configuration`, and any saved coordinate transform in `user_coordinates`.
84
+ - `user_coordinates` (within `raster_metadata`): A `CoordinateTransform` that captures how user coordinates map to machine space, including the UUID and `name` of the transform, the XYZ `offset` applied, the axis/sign remapping in `mapping`, the `last_used` timestamp, and optional operator `notes`.
85
+ - `n_refs` and `n_samples`: Counts of final reference and sample pulses stored in the database (stitched sources are excluded so the numbers reflect the user-facing traces).
86
+
87
+ ### Understanding the Measurement Data Structure
88
+
89
+ The `load_pulses` function returns a list of `Measurement` objects. Each `Measurement` represents a single pulse measurement with its associated metadata and spatial information.
90
+
91
+ #### Measurement Fields
92
+
93
+ A `Measurement` object contains the following fields:
94
+
95
+ - **`pulse`** (`Trace`): The actual pulse data containing:
96
+ - `time` (list[float]): Time values for the pulse waveform
97
+ - `signal` (list[float]): Signal amplitude values corresponding to each time point
98
+ - `uuid` (UUID): Unique identifier for this pulse
99
+ - `timestamp` (int): Unix timestamp in milliseconds when the pulse was acquired
100
+ - `derived_from` (list[PulseComposition] | None): If this pulse was created by stitching multiple pulses together, this contains information about the source pulses
101
+ - `averaged_from` (list[Trace] | None): If this pulse was created by averaging multiple pulses, this lists the contributing source traces (which may themselves be stitched)
102
+
103
+ - **`point`** (`Point3D`): The 3D spatial coordinates where this pulse was measured:
104
+ - `x` (float | None): X coordinate
105
+ - `y` (float | None): Y coordinate
106
+ - `z` (float | None): Z coordinate
107
+
108
+ - **`variant`** (str): The type of measurement, one of:
109
+ - `"reference"`: Reference measurement taken at a known location
110
+ - `"sample"`: Sample measurement taken on the object being scanned
111
+ - `"noise"`: Noise measurement for baseline correction
112
+ - `"other"`: Other types of measurements
113
+
114
+ - **`reference`** (UUID | None): If this is a sample measurement, this field may contain the UUID of the associated reference pulse
115
+
116
+ - **`annotations`** (list[KVPair] | None): Optional key-value pairs for additional metadata
117
+
118
+ #### Example: Accessing Measurement Data
119
+
120
+ ```python
121
+ from pathlib import Path
122
+ from grdb import load_pulses
123
+
124
+ db_path = Path("raster.grf")
125
+
126
+ # Load sample pulses
127
+ samples = load_pulses(db_path, offset=0, limit=10, variant="sample")
128
+
129
+ # Access the first measurement
130
+ first_measurement = samples[0]
131
+
132
+ # Access pulse waveform data
133
+ time_values = first_measurement.pulse.time
134
+ signal_values = first_measurement.pulse.signal
135
+
136
+ # Access spatial coordinates
137
+ x_position = first_measurement.point.x
138
+ y_position = first_measurement.point.y
139
+ z_position = first_measurement.point.z
140
+
141
+ # Check the measurement type
142
+ measurement_type = first_measurement.variant
143
+
144
+ # Get the pulse UUID
145
+ pulse_id = first_measurement.pulse.uuid
146
+ ```
grdb-0.9.1/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # grdb
2
+
3
+ A Python library for creating and managing SQLite databases of THz pulse measurement data.
4
+
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ pip install grdb
10
+ ```
11
+
12
+
13
+ ## Examples
14
+
15
+ ### Loading Pulses
16
+
17
+ **Load sample pulses:**
18
+ ```python
19
+ from pathlib import Path
20
+ from grdb import load_pulses
21
+
22
+ db_path = Path("raster.grf")
23
+
24
+ # Load first 100 sample pulses
25
+ samples = load_pulses(db_path, offset=0, limit=100, variant="sample")
26
+ ```
27
+
28
+ **Load reference pulses:**
29
+ ```python
30
+ from pathlib import Path
31
+ from grdb import load_pulses
32
+
33
+ db_path = Path("raster.grf")
34
+
35
+ # Load first 50 reference pulses
36
+ references = load_pulses(db_path, offset=0, limit=50, variant="reference")
37
+ ```
38
+
39
+ **Load all pulses (both samples and references):**
40
+ ```python
41
+ from pathlib import Path
42
+ from grdb import load_pulses
43
+
44
+ db_path = Path("raster.grf")
45
+
46
+ # Load all pulse types (variant=None)
47
+ all_pulses = load_pulses(db_path, offset=0, limit=1000, variant=None)
48
+ ```
49
+
50
+ ### Loading Metadata
51
+
52
+ ```python
53
+ from pathlib import Path
54
+ from grdb import load_metadata
55
+
56
+ db_path = Path("raster.grf")
57
+
58
+ raster_config, device_metadata, raster_metadata, n_refs, n_samples = load_metadata(db_path)
59
+ ```
60
+
61
+ - `raster_config`: Acquisition settings such as raster `patterns`, scan `stepsize`, optional `reference_point`, how often to `acquire_ref_every`, and any multi-pass `repetitions_config`.
62
+ - `device_metadata`: Instrument identifiers including the serial number and firmware version captured at acquisition time.
63
+ - `raster_metadata`: Session-level context like the GRDB app version, optional `raster_id`, acquisition `timestamp`, user-supplied `annotations`, raw `device_configuration`, and any saved coordinate transform in `user_coordinates`.
64
+ - `user_coordinates` (within `raster_metadata`): A `CoordinateTransform` that captures how user coordinates map to machine space, including the UUID and `name` of the transform, the XYZ `offset` applied, the axis/sign remapping in `mapping`, the `last_used` timestamp, and optional operator `notes`.
65
+ - `n_refs` and `n_samples`: Counts of final reference and sample pulses stored in the database (stitched sources are excluded so the numbers reflect the user-facing traces).
66
+
67
+ ### Understanding the Measurement Data Structure
68
+
69
+ The `load_pulses` function returns a list of `Measurement` objects. Each `Measurement` represents a single pulse measurement with its associated metadata and spatial information.
70
+
71
+ #### Measurement Fields
72
+
73
+ A `Measurement` object contains the following fields:
74
+
75
+ - **`pulse`** (`Trace`): The actual pulse data containing:
76
+ - `time` (list[float]): Time values for the pulse waveform
77
+ - `signal` (list[float]): Signal amplitude values corresponding to each time point
78
+ - `uuid` (UUID): Unique identifier for this pulse
79
+ - `timestamp` (int): Unix timestamp in milliseconds when the pulse was acquired
80
+ - `derived_from` (list[PulseComposition] | None): If this pulse was created by stitching multiple pulses together, this contains information about the source pulses
81
+ - `averaged_from` (list[Trace] | None): If this pulse was created by averaging multiple pulses, this lists the contributing source traces (which may themselves be stitched)
82
+
83
+ - **`point`** (`Point3D`): The 3D spatial coordinates where this pulse was measured:
84
+ - `x` (float | None): X coordinate
85
+ - `y` (float | None): Y coordinate
86
+ - `z` (float | None): Z coordinate
87
+
88
+ - **`variant`** (str): The type of measurement, one of:
89
+ - `"reference"`: Reference measurement taken at a known location
90
+ - `"sample"`: Sample measurement taken on the object being scanned
91
+ - `"noise"`: Noise measurement for baseline correction
92
+ - `"other"`: Other types of measurements
93
+
94
+ - **`reference`** (UUID | None): If this is a sample measurement, this field may contain the UUID of the associated reference pulse
95
+
96
+ - **`annotations`** (list[KVPair] | None): Optional key-value pairs for additional metadata
97
+
98
+ #### Example: Accessing Measurement Data
99
+
100
+ ```python
101
+ from pathlib import Path
102
+ from grdb import load_pulses
103
+
104
+ db_path = Path("raster.grf")
105
+
106
+ # Load sample pulses
107
+ samples = load_pulses(db_path, offset=0, limit=10, variant="sample")
108
+
109
+ # Access the first measurement
110
+ first_measurement = samples[0]
111
+
112
+ # Access pulse waveform data
113
+ time_values = first_measurement.pulse.time
114
+ signal_values = first_measurement.pulse.signal
115
+
116
+ # Access spatial coordinates
117
+ x_position = first_measurement.point.x
118
+ y_position = first_measurement.point.y
119
+ z_position = first_measurement.point.z
120
+
121
+ # Check the measurement type
122
+ measurement_type = first_measurement.variant
123
+
124
+ # Get the pulse UUID
125
+ pulse_id = first_measurement.pulse.uuid
126
+ ```
@@ -0,0 +1,23 @@
1
+ __version__ = "0.9.1"
2
+
3
+ # Re-export public API functions for convenient imports, e.g.:
4
+ from .crud import (
5
+ add_pulses,
6
+ create_db,
7
+ load_metadata,
8
+ load_pulses,
9
+ update_annotations,
10
+ update_references,
11
+ )
12
+ from .devtools import make_dummy_database
13
+
14
+ __all__ = [
15
+ "__version__",
16
+ "add_pulses",
17
+ "create_db",
18
+ "load_metadata",
19
+ "load_pulses",
20
+ "make_dummy_database",
21
+ "update_annotations",
22
+ "update_references",
23
+ ]
@@ -0,0 +1,27 @@
1
+ from sqlalchemy import Engine, Table
2
+ from sqlmodel import SQLModel
3
+
4
+ from grdb.models import (
5
+ PulseCompositionTable,
6
+ PulseDB,
7
+ RasterInfoDB,
8
+ SchemaVersion,
9
+ )
10
+
11
+
12
+ def create_tables(engine: Engine) -> None:
13
+ """Create the necessary tables in the database."""
14
+ SQLModel.metadata.create_all(engine, tables=_get_tables())
15
+
16
+
17
+ def _get_tables() -> list[Table]:
18
+ """Get the names of all tables in the database."""
19
+ return [
20
+ SQLModel.metadata.tables[table_name]
21
+ for table_name in [
22
+ RasterInfoDB.__tablename__,
23
+ PulseDB.__tablename__,
24
+ PulseCompositionTable.__tablename__,
25
+ SchemaVersion.__tablename__,
26
+ ]
27
+ ]