Tidal harmonic analysis · reference & data registry Data access Sign in Contact
tidalcore water-level time-series analysis - documentation & datasets release 0.9.3 · MIT

Home › reference › formats and qc

Data formats and QC flags

Gauge data arrives in whatever shape the agency that collected it chose thirty years ago. tidalcore reads six layouts and refuses to guess about four things. The refusals matter more than the readers, so they come first.

What the readers will not guess

  1. Time zone. If timestamps carry no offset and tz was not passed, the reader raises TimeZoneUnknownError. Assuming UTC on a local-time file shifts every phase by a constant and nothing downstream will notice.
  2. Units. A column of numbers around 200 could be centimetres or a record in feet from a very deep harbour. The unit must be in the header or in the call.
  3. Duplicate timestamps. Two rows with the same instant raise FormatError with the line number. Deciding which of the two is correct is not the library's business.
  4. Order. A non-monotonic time column is an error rather than something to sort quietly, because the usual cause is two concatenated files with an overlap.

Readers

Pass an id as format=, or leave the default "auto" and let the sniffer decide from the first non-comment lines.
Format idLayoutDetected byNotes
csv2timestamp, leveltwo comma-separated fieldsflags default to 0, unchecked
csv-flaggedtimestamp, level, flagthree fields, third is 0 to 9the format used in the tutorial
tsv2tab-separated, same as csv2tab in the first data lineaccepts a leading comment block
fixed-24one line per day, 24 fields of five charactersline length and a numeric date prefixhourly only; missing values are a sentinel, declared in the header
jsonlone object per line with keys t, v, qfirst byte is an opening bracestreams without loading the file
netcdf-cfCF conventions, a time coordinate and one level variablefile magicneeds the netcdf extra; the time units attribute must state a zone

Sniffing reads at most the first 64 kilobytes and never the whole file, which means a file that changes shape halfway through will be read with the wrong reader and then fail on a line number. That is the intended outcome: a loud failure at line 40 311 is better than a quiet reinterpretation.

rec = tc.read_gauge("harbour-1958.txt", format="fixed-24", tz="+01:00")
print(rec.units, rec.reference, rec.step)
# m  admiralty chart datum  60min

Note what happened there: the reference string was copied verbatim from the file header and is carried through to the solution. tidalcore does not know what it means and does not convert anything to it or from it.

Quality-control flags

One uint8 per sample. The vocabulary is small on purpose, and the only values the library writes by itself are 3, 5 and 9.

Default weights. Change them with analyse(..., weights={2: 0.5}), and record the fact next to any published amplitude.
FlagMeaningIn the fit by defaultWho sets it
0uncheckedyes, weight 1readers, when a file has no flag column
1goodyes, weight 1the data provider
2probably goodyes, weight 1the data provider
3probably badnoqc.flag_spikes and qc.flag_flat
4badnothe data provider, or you by hand
5filled from a solutionnofill_gaps
9missingno, the value is NaNqc.flag_gaps and the readers

Flag 0 is admitted to the fit and flag 2 is too. That is a deliberate choice for a tool whose main input is decades-old records with no quality column at all, and it is also the setting most likely to surprise you. Solution.weights_used always prints the mapping that was actually applied.

Time, units and gaps

Timestamps
ISO 8601 with an offset, parsed to numpy.datetime64 in UTC at microsecond resolution. Leap seconds are not represented, consistent with the underlying type.
Levels
Floating point in the file's own unit, positive upwards. A negative level is perfectly normal and means below the stated reference.
Gaps
NaN in level and flag 9 in flags. The two always agree; a NaN with flag 1 is rejected at read time as an internal inconsistency.
Sampling
Regular or irregular. step is the median interval and is reported, not enforced; the fit uses the timestamps as given.

Output

A solution written with to_json is a flat object, deliberately boring, and stable enough to parse with a shell one-liner:

{
  "tidalcore": "0.9.3",
  "station": "station-7",
  "units": "m",
  "reference": "station zero",
  "ref_time": "2026-04-18T12:00:00Z",
  "nodal": true,
  "coverage": 0.989,
  "n_used": 88050,
  "constituents": {
    "M2": {"amplitude": 1.412, "phase": 211.6, "error": 0.0044},
    "S2": {"amplitude": 0.463, "phase": 238.2, "error": 0.0044}
  }
}

The key names may change once before version 1.0, as noted on the overview; a converter will ship in the release that changes them.

See also: the tutorial shows the flaggers in sequence on a real-shaped record, and the API overview lists every exception a reader can raise.