LJH Memorial File Format

LJH Version 2.2.0 is the current LJH file format.

The LJH file format consists of a human-readable ASCII header followed by an arbitrary number of binary data records. Information in the header specifies the exact length in bytes of each data record.

Header Information

The human-readable ASCII header is the start of the LJH file. That means you can say less myfile_chan5.ljh at a unix terminal and get meaningful information about the file…before the gibberish starts. Handy, right?

The header is somewhat fragile (it would have been better written in YAML or TOML or even JSON, but we decided just to live with it). It consists essentially of key-value pairs, with a format of Key: value, one pair per line.

Header Notes:

  • Lines begining with # are usually ignored.

  • #End of Header marks the end of the header and the beginning of the binary data.

  • #End of Description has special meaning if System description of this File: has been read.

  • Newlines are the newlines of the digitizing computer. The interpreting program must accept LF, CR, or CRLF.

  • Capitalization must be matched.

  • One space follows a colon. Additional spaces are treated as part of the value.

  • Programs that read LJH files ignore header keys that are unexpected or unneeded.

#LJH Memorial File Format

This line indicates that the file is based on format described here.

Save File Format Version: 2.2.0
Software Version: DASTARD version 0.2.15
Software Git Hash: 85ab821
Data source: Abaco

These lines uniquely identify the exact format, so the interpreting program can adapt. While the first line should be sufficient for this purpose, the second and third lines take in the possibility that a particular program may have a bug. The interpreting program may be aware of this bug and compensate. The Data source is meant for later human reference.

Number of rows: 74
Number of columns: 1
Row number (from 0-73 inclusive): 12
Column number (from 0-0 inclusive): 0
Number of channels: 74
Channel name: chan12
Channel: 12
ChannelIndex (in dastard): 12

Dastard inserts this information to help downstream analysis tools understand the array being used when this file was acquired.

Digitized Word Size in Bytes: 2

Each sample is stored in this many bytes.

Location: LLNL
Cryostat: C3PO
Thermometer: GRT1
Temperature (Ohm or K): 0.1
Bridge range: 20.0E3
Magnetic field (A or Gauss): 0.75
Detector: SnTES#8
Sample: Orange peel
Excitation/Source: none
Operator: Leisure Larry

Like the several lines above, most lines are comments for later human use and are not interpreted by general-purpose LJH readers.

System description of this File:
blah
blah
blah
User description of this File:
blah
blah
blah
#End of Description

This is a multiline comment. Once the ‘’Description of this File:’’ line is read, all following lines are concantenated until ‘’#End of Description’’ is read.

Again, this is ignored by programs that read LJH files.

Number of Digitizers: 1
Number of Active Channels: 2

The number of digitizers and channels present in the file are given so that space may be allocated for them by the interpreting program, if necessary.

Timestamp offset (s): 3016738980.049000

The meaning of this and the means of interpreting it are dependent upon the particular programs creating and reading this file. It was a necessary offset in earlier versions of LJH, where we did not reserve enough bytes per record to record a full timestamp. In LJH 2.2, it serves as a simple zero-time (all records should be no earlier than this “offset”).

Server Start Time: 18 Nov 2022, 15:47:34 MST
First Record Time: 18 Nov 2022, 16:54:15 MST

These times show when the server (Dastard, in this case) started running, and when the first record was written to this file.

Timebase: 5.000000E-8
Number of samples per point: 1

Timebase gives the sampling period (in seconds). Number of samples per point is generally 1, but can be more in special cases where samples are averaged and downsampled before recording.

Presamples: 256
Total Samples: 1024

Total samples is the actual record length in samples. The trigger point will be located at sample number Presamples.

Binary Information

If you read an LJH file until the characters #End of Header, then the remainder of the file is the binary section. It consists of a sequence of data records.

Each record starts with a 16-byte time marker. The record’s waveform data consists of the next L*M bytes, where L is the number of samples (Total Samples: value from the header) and M is the number of bytes per sample (Digitized Word Size in Bytes: from the header). M is always 2 bytes per sample, in practice.

  • The full record’s length is 16+L*M .

  • All values in the data record are little endian.

  • The first 8-byte word is the row counter. It counts the number of row times read out since the server started. If the server has to resynchronize on the raw data, then the row counter will be incremented by an estimate to account for the time missed.

  • The second 8-byte word is the POSIX microsecond time, i.e., the time in microseconds since 1 January 1970 00:00 UT. (Warning: this will overflow in 292,226 years if you interpret it as a signed number.)

  • The next L words (of M bytes each) are the data record, as a signed or unsigned integer. (Typically, we use signed for the TDM error signal and unsigned for the TDM feedback, and unsigned for µMUX data.)

Binary Information (for LJH version 2.1.0)

Version 2.1.0 follows. Warning! You probably want 2.2.0.

Each record starts with a 6-byte time marker. The record’s waveform data consists of the next L*M bytes, where L is the number of samples (Total Samples: value from the header) and M is the number of bytes per sample (Digitized Word Size in Bytes: from the header). M is always 2 bytes per sample, in practice.

  • The full record’s length is 6+L*M .

  • All values in the data record are little endian.

  • The first byte is a “4 microsecond tick”. That is, it counts microseconds past the millisecond counter and records the count divided by 4. Beware that previous versions of LJH used the first byte to signify something about the type of data. Igor seems to ignore this byte, though, so I think we’re okay to stuff timing information into it.

  • The second byte used to signify a channel number N, which corresponds to the Nth channel described in the header. Channel number 255 is reserved for temperature readout with the DMM. Since 2010, this has always been meaningless.

  • The next 4 bytes are an unsigned 32-bit number that is the value of a millisecond counter on the digitizing computer.

  • The next L words (of M bytes each) are the data record, as a signed or unsigned integer. (Typically, we use signed for the TDM error signal and unsigned for the TDM feedback.)

Changelog

  • Version 2.2.0 (6 Aug 2015) Changed the binary definition to include 8 bytes each record for pulse timing (microsecond precision) and frame number.

  • Version 2.1.0 (23 Sep 2011) Used the first byte of each record to get 4 microsec timing resolution instead of 1 ms.

  • Version 2.0.0 (27 Mar 2011) Defined inversion and offset more clearly

  • Version 2.0.0 (5 Jan 2001) Changed definition of discrimination level

  • Version 2.0.0 (24 May 2000) since most PCs have least significant byte first, the binary information has been changed to default

  • Version 1.1.0 (8 May 2000) added a few more user and channel parameters as well as provisions for temperature monitoring

  • Initial 1.0.0 (5 Aug 1999) definition by Larry J. Hiller