BlackchirpCSV

BlackchirpCSV is the workhorse persistence class for experiment CSV I/O. It owns the canonical experiment-directory layout and provides the static write helpers, directory helpers, and format utilities that every storage class in the persistence subsystem calls directly. The companion DataStorageBase class holds a BlackchirpCSV instance and delegates all file access through it.

All CSV files produced by Blackchirp use the semicolon delimiter (BC::CSV::del); the pipe character (BC::CSV::altDel) is reserved for QStringList values embedded within a single cell. The full set of canonical filenames for an experiment directory is enumerated in the BC::CSV namespace: experiment-root files (versionFile, validationFile, objectivesFile, hwFile, headerFile, chirpFile, markersFile, clockFile, auxFile), FID artifacts (fidparams, fidDir), and LIF artifacts (lifparams, lifDir). Version-key constants (majver, minver, patchver, relver, buildver) are written to and read from version.csv. The six header-column name constants (ok, ak, ai, vk, vv, vu) define the layout of header.csv.

The class has two construction paths. The default constructor creates an instance suitable for static-method use and for writing new experiments; the delimiter defaults to BC::CSV::del. The (num, path) constructor reads version.csv from the experiment directory identified by num and path, populating an internal configuration map that the instance-level readLine and readFidLine methods then use to tokenize subsequent reads with the correct delimiter. The version accessors (majorVersion, minorVersion, patchVersion, releaseVersion, buildVersion) expose the version metadata loaded by this constructor.

API Reference

class BlackchirpCSV

Workhorse persistence class for experiment CSV I/O.

BlackchirpCSV owns two complementary roles. In its static form it provides the write helpers, directory helpers, and format utilities that every storage class calls directly — none of those functions require an instance. In its instance form it holds a snapshot of the version metadata read from a saved experiment’s version.csv, which the loader passes to DataStorageBase subclasses via readLine and readFidLine.

The on-disk layout that these helpers enforce is described by the canonical filename constants in the BC::CSV namespace: experiment-root files (headerFile, chirpFile, clockFile, etc.), FID artifacts in the fidDir subdirectory, and LIF artifacts in the lifDir subdirectory. All CSV files use the semicolon delimiter (BC::CSV::del); the alternative pipe delimiter (BC::CSV::altDel) is reserved for QStringList fields embedded within a cell.

See DataStorageBase and its subclasses for the classes that build on this foundation.

Public Types

enum class XYFormat

Output format for writeXY().

Semicolon, Comma, and Tab write a plain two-column file with the corresponding delimiter. Aligned writes right-justified, whitespace-padded columns for human readability (and is read back with a \s+ separator).

Values:

enumerator Semicolon
enumerator Comma
enumerator Tab
enumerator Aligned

Public Functions

BlackchirpCSV()

Constructs a default instance with no version metadata loaded.

BlackchirpCSV(const int num, const QString path)

Constructs an instance by reading version metadata from a saved experiment.

Opens version.csv in the experiment directory and populates the internal configuration map. The delimiter is detected from the first line of that file so that experiments saved with older delimiter conventions are read correctly.

Parameters:
  • numExperiment number. Combined with path to locate the experiment directory.

  • path – Base data path override. When empty, the path stored in SettingsStorage is used.

QVariantList readLine(QIODevice &device)

Reads and tokenizes the next line from device using the loaded delimiter.

The delimiter is the one detected when this instance was constructed from a saved experiment. On the default-constructed instance it is BC::CSV::del.

Parameters:

device – Open, readable device positioned at the start of a line.

Returns:

List of field values, one QVariant per column; empty if the line is blank.

QVector<qint64> readFidLine(QIODevice &device)

Reads and decodes a FID data line from device.

Parameters:

device – Open, readable device positioned at the start of a FID data line.

Returns:

Vector of raw 64-bit sample values decoded from base-36 encoding.

int majorVersion() const

Returns the major version number from the loaded version metadata.

Returns:

Major version, or -1 if no metadata was loaded.

int minorVersion() const

Returns the minor version number from the loaded version metadata.

Returns:

Minor version, or -1 if no metadata was loaded.

int patchVersion() const

Returns the patch version number from the loaded version metadata.

Returns:

Patch version, or -1 if no metadata was loaded.

QString releaseVersion() const

Returns the release label from the loaded version metadata.

Returns:

Release label string (e.g., "alpha"), or an empty string if not present.

QString buildVersion() const

Returns the build identifier from the loaded version metadata.

Returns:

Build identifier string, or an empty string if not present.

Public Static Functions

static bool writeXY(QIODevice &device, const QVector<QPointF> d, const QString prefix = "", XYFormat fmt = XYFormat::Semicolon)

Writes a single XY data set to device as a two-column file.

Parameters:
  • device – Output device, which must not already be open.

  • d – Vector of points to write.

  • prefix – Optional column-name prefix; when non-empty, column headers become prefix_x and prefix_y instead of x and y.

  • fmt – Column delimiter / layout (see XYFormat).

Returns:

true on success.

static bool writeMultiple(QIODevice &device, const std::vector<QVector<QPointF>> &l, const std::vector<QString> &n = {})

Writes multiple XY data sets side by side as a multi-column CSV.

Parameters:
  • device – Output device, which must not already be open.

  • l – Vector of point-vectors, one per data set.

  • n – Optional column-name prefixes, one per data set. Sets without a matching entry receive auto-generated names (x0/y0, x1/y1, …).

Returns:

true on success.

template<typename T>
static inline bool writeY(QIODevice &device, const QVector<T> d, QString title = "")

Writes a single-column Y-only CSV with an optional title row.

Template Parameters:

T – Element type; must be convertible via QVariant::toString.

Parameters:
  • device – Output device, which must not already be open.

  • d – Values to write, one per row.

  • title – Column header text; defaults to "y".

Returns:

true on success.

template<typename T>
static inline bool writeYMultiple(QIODevice &device, std::initializer_list<QString> titles, std::initializer_list<QVector<T>> l)

Writes multiple single-column Y-only data sets side by side.

Template Parameters:

T – Element type; must be convertible via QVariant::toString.

Parameters:
  • device – Output device, which must not already be open.

  • titles – Column header labels, one per data set.

  • l – Data vectors, one per data set. Must match the size of titles.

Returns:

true on success; false if titles and l have different sizes or the device cannot be opened.

static bool writeHeader(QIODevice &device, const std::multimap<QString, std::tuple<QString, QString, QString, QString, QString>> header)

Writes a full experiment header map as a six-column CSV.

Column order matches the BC::CSV constants: ok, ak, ai, vk, vv, vu.

Parameters:
  • device – Output device, which must not already be open.

  • header – Multimap from object key to a tuple of (array key, array index, value key, value, units).

Returns:

true on success.

static void writeLine(QTextStream &t, const std::vector<QVariant> l)

Writes a single delimited row to an already-open QTextStream.

Appends a newline (BC::CSV::nl) after the last field.

Parameters:
  • t – Destination stream.

  • l – Values to write; each is converted via QVariant::toString.

static QString formatInt64(qint64 n)

Formats a 64-bit integer as a base-36 string.

FID raw sample values are stored in base 36 to keep file sizes compact.

Parameters:

n – Value to format.

Returns:

Base-36 string representation, with a leading "-" for negative values.

static void writeFidList(QIODevice &device, const FidList l)

Writes a list of FID objects as a multi-column base-36 CSV.

The header row names columns fid0, fid1, etc. Sample values are encoded with formatInt64.

Parameters:
  • device – Output device, which must not already be open.

  • l – FID list to serialize; each FID becomes one column.

static bool writeVersionFile(int num)

Writes the version.csv file for the given experiment.

Records the Blackchirp version constants (majver, minver, patchver, relver, buildver) so that future reads can detect the format version.

Parameters:

numExperiment number.

Returns:

true on success.

static bool exptDirExists(int num)

Tests whether the directory for the given experiment exists.

Parameters:

numExperiment number.

Returns:

true if the experiment directory exists on disk.

static int scanMaxExptNumOnDisk(const QString &basePath = QString())

Scans the data path for the highest experiment number on disk.

Walks the rightmost branch of the <basePath>/experiments/mil/th/num hierarchy. Used to keep the stored exptNum reconciled with what is actually on disk so that users who switch acquisition app versions, or modify the data tree outside the program, do not allocate a duplicate experiment number.

Parameters:

basePath – Optional base path. When empty, the active savePath from settings is used.

Returns:

Highest experiment number under <basePath>/experiments, or 0 if no numeric subdirectory was found.

static void mirrorExptNumToV1Settings(int num)

Mirrors the next-experiment counter into the v1.x settings store.

Temporary cross-version coupling for the v2.x pre-release window: users who hit a regression may need to fall back to the v1.x acquisition app, which predates the per-major-version applicationName convention and so reads the unsuffixed Blackchirp.conf rather than v2.x’s Blackchirp2.conf. Mirroring the counter prevents v1.x from allocating a number that already exists on disk.

The mirror is a no-op when v1.x’s savePath differs from v2.x’s, since exptNum is per-tree — writing v2.x’s counter into v1.x’s settings would corrupt v1.x’s own counter for its own data tree.

Note

Safe to remove once v1.x is no longer a supported fallback.

Parameters:

numExperiment number that v2.x has just allocated.

static bool createExptDir(int num)

Creates the directory hierarchy for the given experiment.

The hierarchy follows the pattern savePath/experiments/mil/th/num, where mil and th are the million- and thousand-buckets of num.

Parameters:

numExperiment number.

Returns:

true if the directory was created or already existed.

static QDir exptDir(int num, QString path = "")

Returns a QDir pointing to the given experiment’s root directory.

Parameters:
  • numExperiment number.

  • path – Optional base path override. When empty, SettingsStorage provides the application data path.

Returns:

QDir for the experiment root (may not exist).

static QDir logDir()

Returns a QDir pointing to the application log directory.

Returns:

QDir for the log directory (may not exist).

static QDir textExportDir()

Returns a QDir pointing to the text-export output directory.

Returns:

QDir for the text-export directory (may not exist).

static QDir trackingDir()

Returns a QDir pointing to the hardware-tracking data directory.

Returns:

QDir for the tracking directory (may not exist).

Private Members

std::map<QString, QVariant, std::less<>> d_configMap
QString d_delimiter