gnssrefl.tracks_qc module
Quality-control edits for tracks.json / vwc_tracks.json files.
Available operations:
split_epoch/merge_epochs: subdivide one epoch into two at a chosen MJD, or combine two adjacent epochs back into one. Use when hardware (receiver or satellite) or significant environmental changes occur that motivate a new apriori RH on the same geometric track.ignore_range/unignore_range: mark (or un-mark) a time window within an epoch so its arcs are excluded from fits and stats.deactivate_epoch: turn an epoch off without deleting it, so downstream tools skip it.delete_track: drop a track entirely from this file onward.save_tracks: write the JSON back after refitting every active epoch and (for vwc_tracks files) recomputingapriori_RH/RH_stdfrom the arcs. Read withtracks.load_tracks_json.
Edits are applied to an in-memory dict and only become a self-
consistent file after save_tracks runs the refit + stats pass.
Structurally invalid edits raise ValueError.
- gnssrefl.tracks_qc.apply_auto_removal(tracks_json, fr, good_keys)
Drop bad tracks/epochs at frequency fr via the QC primitives.
For every track whose freq equals fr, walks its active epochs. Epochs whose (track_id, track_epoch) is not in good_keys are removed: the whole track goes via delete_track when none of its active epochs survive, otherwise each bad epoch goes via deactivate_epoch. Tracks on other frequencies are untouched.
- Parameters:
tracks_json (dict) – vwc_tracks.json document (mutated in place).
fr (int) – Frequency to filter. Only tracks with track[‘freq’] == fr are considered.
good_keys (set of (int, int)) – (track_id, track_epoch) pairs that passed the run’s QC.
- Returns:
{‘tracks_removed’, ‘tracks_total’, ‘epochs_deactivated’, ‘epochs_total’}. Totals count only tracks/epochs at fr.
- Return type:
dict
- gnssrefl.tracks_qc.check_track_exists(tracks_json, track_id)
- gnssrefl.tracks_qc.compute_tracks_stats(tracks_json, arcs_df, apriori_rh_ndays)
Compute
n_qc_arcsand apriori_RH/RH_std on active epochs.Inactive epochs are skipped; their
n_qc_arcsis zeroed at deactivation time.
- gnssrefl.tracks_qc.deactivate_epoch(tracks_json, track_id, epoch_id)
Mark an active epoch inactive. Refit/stats will skip it on save.
Zeros
n_arcsandn_qc_arcsso inactive epochs carry the invariant “0 arcs live”.epoch_idis scoped to this saved tracks_json; see tracks.py module docstring.
- gnssrefl.tracks_qc.delete_track(tracks_json, track_id)
Remove a track entirely from tracks_json[‘tracks’].
track_idis the geometric identity and is stable across saves (see tracks.py module docstring); deleting it means that id is gone from this snapshot onward.
- gnssrefl.tracks_qc.filter_by_ignored_ranges(sub, ranges)
Return
subwith rows whose mjd falls in any ignored range removed.
- gnssrefl.tracks_qc.find_active_epoch_containing(track, mjd)
- gnssrefl.tracks_qc.ignore_range(tracks_json, track_id, epoch_id, mjd_start, mjd_end)
Append
[mjd_start, mjd_end]to the target active epoch’s ignored ranges.Range must lie inside the epoch window and satisfy
mjd_end > mjd_start.epoch_idis scoped to this saved tracks_json; see tracks.py module docstring.
- gnssrefl.tracks_qc.merge_epochs(tracks_json, track_id, epoch_id_a, epoch_id_b)
Merge two adjacent active epochs with matching constellation / match_T.
Window becomes the union;
ignored_rangesare concatenated.anchor_time/repeat_interval_dinherit from the earlier epoch and are refit on save. Renumbers all epochs after the merged pair (epoch_id is scoped to this saved tracks_json; see tracks.py module docstring).
- gnssrefl.tracks_qc.recompute_derived_fields(tracks_json, arcs_df)
Refit active epochs and refresh every derived per-epoch field.
For
file_type == 'vwc_tracks'JSON, also recomputes each epoch’sapriori_RH/RH_std/n_qc_arcs. The order matters:recompute_n_arcsandcompute_tracks_statsshare the same ignored-range mask logic and must agree on every epoch.
- gnssrefl.tracks_qc.recompute_durations(tracks_json)
Refresh
duration_dfor every epoch from current start/end times.
- gnssrefl.tracks_qc.recompute_metadata_aggregates(tracks_json, arcs_df)
Refresh top-level metadata totals and the data time range.
Writes
n_tracks,n_epochs,n_arcs(andn_qc_arcsfor vwc_tracks) andstart_time/end_time/duration_donmetadata. Rebuilds metadata key order so the totals sit together. Removes the legacy nestedtime_rangefield.
- gnssrefl.tracks_qc.recompute_n_arcs(tracks_json, arcs_df)
Set
n_arcson every active epoch from arcs_df.Counts arcs whose
(track_id, track_epoch)matches the epoch and whosemjdis outside anyignored_ranges. Writes0when no arcs match. Inactive epochs are skipped; theirn_arcsis zeroed at deactivation time.
- gnssrefl.tracks_qc.refit_active_epochs(tracks_json, arcs_df)
For each active epoch, refit anchor_time / repeat_interval_d via fit_segment.
- gnssrefl.tracks_qc.renumber_epoch_ids(track)
- gnssrefl.tracks_qc.reorder_epoch_keys(epoch)
Rewrite
epochin place with keys in canonical order.
- gnssrefl.tracks_qc.require_active_epoch(track, track_id, epoch_id)
- gnssrefl.tracks_qc.save_tracks(tracks_json, path, tool, note='', arcs_df=None)
Append a history entry, refresh every derived field, and write the JSON.
For
file_type == 'vwc_tracks'JSON, also recomputes each epoch’sapriori_RHandRH_stdfrom arcs in that epoch’s trailingapriori_rh_ndayswindow. Writes atomically (temp file + rename).arcs_dfcan be passed by callers that already have the walked arcs (avoids re-reading the results files). IfNone, arcs are loaded via load_gnssir_results_from_tracks.
- gnssrefl.tracks_qc.split_epoch(tracks_json, track_id, split_mjd)
Split one active epoch into two adjacent actives at
split_mjd.split_mjdmust lie strictly inside the target epoch’s window. Both halves inherit the original epoch’s fit parameters (anchor_time, repeat_interval_d, az_avg_minel, az_drift_rate); fresh values come fromsave_tracks’ refit pass.
- gnssrefl.tracks_qc.unignore_range(tracks_json, track_id, epoch_id, mjd_start, mjd_end)
Subtract
[mjd_start, mjd_end]from the epoch’s ignored ranges.Raises if the subtraction leaves every existing range unchanged (i.e. no overlap anywhere).
epoch_idis scoped to this saved tracks_json; see tracks.py module docstring.
- gnssrefl.tracks_qc.validate_epoch_ids(tracks_json)
Enforce that each track’s epoch ids are exactly 0..N-1 in order.
Raises ValueError on the first violation.