Skip to content

Axon ABF

ABF files are written by Clampex and AxoScope (pCLAMP) for patch-clamp recordings. OpenReadout exposes a file as one trace: sweep_count sweeps × input channels, every sample scaled to the channel’s physical unit (pA, mV, …). Sweeps are the episodes of an episodic protocol; gap-free recordings have one sweep. ABF 1 (1.3–1.84) and ABF 2 (2.0–2.9) are read, gap-free and episodic, float32 and int16. Clampfit’s ATF text exports are covered at the end of this page.

Derived from Scott Harden’s public, MIT-licensed pyABF documentation and source (pyABF 2.3.8, commit 3ad9cd6) and public corpus files; pyABF is also used as a reference reader. See docs/provenance/abf.md.

All numbers are little-endian. Offsets below are bytes from the start of the file (ABF 1) or of the section (ABF 2).

Detection

Bytes 0–3: ABF (ABF 1) or ABF2 (ABF 2). Both are definite signatures. Older CLPX/FTCX pCLAMP files are not read.

ABF 2

Fixed header (512 bytes)

offset type our name use
0 char[4] – ABF2
4 u8[4] version stored build, bugfix, minor, major → 2.6.0.0
12 u32 header_sweep_count sweeps recorded (0 in gap-free files)
16 u32 – date YYYYMMDD → created_at
20 u32 – milliseconds since midnight → created_at
30 u16 sample_format 0 → Int16, 1 → Float32
40 u8[16] guid GUID (first three fields little-endian)
56 u8[4] creator_version writer version, stored reversed
60 u32 – string index of the writer name → creator
72 u32 – string index of the protocol path → protocol_path
76 18 × 16 bytes sections section map (below)

Section map (SECTION_MAP_OFFSET = 76, SECTION_ENTRY_LEN = 16)

Each entry is u32 block, u32 entry_size, i64 entry_count; the section starts at block × 512 (BLOCK_LEN). The 18 entries, in order and in our names (SECTION_NAMES): protocol, adc, dac, epoch, adc_per_dac, epoch_per_dac, user_list, stats_region, math, strings, data, tag, scope, delta, voice_tag, synch_array, annotation, stats. A section’s bytes are entry_size × entry_count, except strings, which is one block of entry_size bytes holding entry_count strings (inferred; see provenance). info --view structure lists every non-empty section with its offset and size.

Sections we decode

protocol (one entry): 0 i16 operation mode (AcquisitionMode: 1 EventVariableLength, 2 EventFixedLength, 3 GapFree, 4 HighSpeedOscilloscope, 5 Episodic, other → Other); 2 f32 sample interval per channel in µs (sample_interval_us; rate = 1e6 / interval); 14 f32 synch time unit in µs (synch_time_unit_us); 62 f32 sweep start-to-start interval in s (sweep_interval_s); 110 f32 ADC range in V (adc_range_v); 118 i32 ADC resolution in counts (adc_resolution); 126 i16 experiment type (experiment_kind_code); 132 i32 string index of the file comment (comment); 182 i16 alternate DAC output state (alternate_dac_output, non-zero = on); 206 i16 digitizer type (digitizer_code).

adc (one entry per recorded channel, in data order → InputChannel): 0 i16 physical ADC number (adc_number); 2 i16 telegraph enabled (== 1); 4 i16 telegraph instrument (instrument_code); 6 f32 telegraph additional gain (additional_gain); 10 f32 telegraph filter (filter_hz); 14 f32 membrane capacitance (membrane_capacitance); 18 i16 telegraph clamp mode (clamp_mode_code); 28 f32 programmable_gain; 40 f32 instrument_scale (V at the ADC per user unit); 44 f32 instrument_offset; 48 f32 signal_gain; 52 f32 signal_offset; 56 f32 lowpass_hz; 60 f32 highpass_hz; 74 i32 string index of the channel name; 78 i32 string index of the unit.

dac (→ OutputChannel): 0 i16 DAC number (index); 12 f32 holding_level; 24 i32 string index of the name; 28 i32 string index of the unit; 40 i16 waveform enabled (waveform_enabled); 42 i16 waveform source (waveform_source_code: 1 epochs, 2 stimulus file); 44 i16 inter-sweep level (inter_episode_last: non-zero = hold the last epoch level between sweeps instead of the holding level); 60 i16 conditioning train enabled (conditioning); 118 i32 string index of the stimulus file path (stimulus_file).

user_list: per entry 2 i16 enabled, 4 i16 parameter to vary; any entry enabled or naming a parameter (> 0) sets user_list_active.

epoch_per_dac (→ Epoch, grouped by DAC): 0 i16 epoch number (index); 2 i16 DAC number; 4 i16 type (kind_code, EpochKind: 0 Off, 1 Step, 2 Ramp, 3 PulseTrain, 4 TriangleTrain, 5 CosineTrain, 7 BiphasicTrain, other Other); 6 f32 first level (level); 10 f32 per-sweep level increment (level_step); 14 i32 first duration in samples (duration); 18 i32 per-sweep duration increment (duration_step); 22 i32 pulse_period; 26 i32 pulse_width.

epoch (→ DigitalEpoch): 0 i16 epoch number (epoch); 2 i16 digital output bits (pattern, bit n = output n; shown as eight binary digits).

strings (→ strings, parse_string_table): SSCH, u32 1, u32 number of strings, two u32 we do not interpret, NUL padding, then NUL-terminated strings. String index 1 is the first string; 0 means none. Bytes are Latin-1 (0xB5 = µ).

data: entry_count samples of entry_size bytes (2 for Int16, 4 for Float32), channels interleaved sample by sample in adc order, sweeps back to back.

synch_array (→ synch): per sweep i32 start, i32 length; start in synch-time units (µs × synch_time_unit_us) or, when the unit is 0, in multiplexed samples; length in multiplexed samples (all channels).

tag (→ Tag): 0 i32 time (raw_time, synch-time units; time_s); 4 char[56] comment; 60 i16 type (kind_code: 0 time, 1 comment, 2 external, 3 voice).

ABF 1

A fixed header: 2048 bytes (ABF1_BASIC_LEN) in files written before version 1.6, 6144 bytes (ABF1_EXTENDED_LEN) after. We read the fields past byte 2048 only when the data starts at or after byte 6144 (header_len), because in old files those offsets are sample data.

offset type use
4 f32 version (1.83 → version)
8 i16 operation mode (as ABF 2)
10 i32 samples in the data, all channels (total_samples)
14 i16 samples ignored at the data start (× sample width added to data_offset)
16 i32 sweeps (header_sweep_count)
20 / 24 i32 / i32 date (YYYYMMDD, or YYMMDD with 80–99 → 19xx) / seconds since midnight → created_at
40 i32 data block → data_offset
44 / 48 i32 / i32 tag block / tag count (64-byte Tag records, as ABF 2)
92 / 96 i32 / i32 synch-array block / entry count
100 i16 sample format (0 int16, 1 float32)
120 i16 channels (1–16)
122 f32 sample interval in µs, multiplexed: per-channel interval = value × channels
130 f32 synch time unit (µs)
178 f32 sweep start-to-start interval (s)
244 / 252 f32 / i32 ADC range (V) / resolution
260 i16 experiment type
294 char[16] creator
310 char[56] comment (older field)
366 i16 milliseconds added to the start time
410 i16[16] physical ADC sampled at each data position: channel i uses per-ADC arrays at index sequence[i]
442 / 602 char[10][16] / char[8][16] ADC names / units
730 / 922 / 986 / 1050 / 1114 f32[16] programmable_gain / instrument_scale / instrument_offset / signal_gain / signal_offset
1178 / 1242 f32[16] lowpass_hz / highpass_hz
1306 / 1346 / 1394 char[10][4] / char[8][4] / f32[4] DAC names / units / holding_level
1436 / 1588 i16 / i16[10] digital outputs enabled / per-epoch digital pattern
1440, 1444–1583 i16, 10-entry tables active DAC and its epoch table (old files: type i16, levels f32, durations i16)
2136 / 2216 i32[2][10] pulse_period / pulse_width (extended)
2296 / 2300 i16[2] waveform enabled / source per DAC (extended)
2308–2667 [2][10] tables epoch type, level, level step, duration, duration step (extended)
2736 char[256][2] stimulus file per DAC (extended)
4512–4799 [16] arrays telegraph enabled, instrument, additional gain, filter, capacitance, clamp mode (extended)
4898 / 5154 char[256] / char[128] protocol_path / comment (extended)
5282 u8[16] guid (extended)
5798 i16[4] creator_version (extended)

The sample rate is 1e6 / interval / channels.

Sweeps (Sweep)

Sweep count = header sweeps, except 1 for gap-free files and when the header says 0. When the synch array has one entry per sweep and the lengths differ (variable-length event acquisition, pyabf-2020-06-16-0000), each sweep’s sample_count is its synch length ÷ channels and sweeps follow one another (first_sample); otherwise the data divides into equal sweeps (a remainder is reported as sweep_length_mismatch). start_s comes from the synch array when present, else sweep × sweep_interval_s (or × the sweep length). Trace sample_count is the longest sweep; extra.sweep_sample_counts lists them when they differ.

Scaling (channel_scaling)

For int16 data, per channel, evaluated in this order in f64:

scale = 1 / instrument_scale / signal_gain / programmable_gain [/ telegraph additional_gain if telegraph enabled] × adc_range_v / adc_resolution
offset = instrument_offset − signal_offset
value = raw × scale + offset

Float32 data is already in physical units (scale 1, offset 0). SignalChannelInfo.scale/offset report the pair. pyABF computes the same chain but applies it in float32; see the provenance log for why the oracle recomputes in f64.

Command waveforms (trace 1; command_plan → CommandPlan {outputs, refused}, command_sweep, command_trace_info)

The DAC command of each sweep is synthesized from the epoch table when the header fixes it completely: ABF 2, episodic stimulation, sweeps of one length, no active user list, no alternating DAC outputs; per DAC: waveform enabled with the epoch table as its source (not a stimulus file), no conditioning train, only off, step, ramp and pulse-train epochs (pulse period > 0), non-negative durations, and the epochs ending inside the sweep. Otherwise there is no trace 1 and info notes say why (refused); the epoch table stays in trace 0 extra.outputs.

A sweep of n samples: the first n / 64 (integer division) samples are the pre-sweep level; then each epoch that is not off, in order, lasts duration + duration_step × sweep samples at level + level_step × sweep; the rest of the sweep is the post-sweep level. The pre-sweep level is the holding level, or with inter_episode_last the last epoch level of the previous sweep (the holding level for sweep 0); the post-sweep level is the holding level, or with inter_episode_last this sweep’s last epoch level. A step holds its level; a ramp goes linearly from the level before it to its own level, both ends included (i × (b − a)/(len − 1) + a, the last sample exactly b); a pulse train holds the level before it and takes the epoch level for pulse_width samples at every multiple of pulse_period (whole periods only). This is the rule pyABF documents and ClampEx follows (the 1/64 pre-sweep holding is ClampEx’s).

Trace 1: name command, same sample_rate_hz, sample_count and sweep_count as trace 0; one float64 channel per synthesized DAC (name = the DAC name or DAC<n>, unit = the DAC unit, channel extra.dac, extra.holding_level); trace extra.synthesized = true, extra.source, extra.not_synthesized (reasons for other DACs). Values are the command in the DAC’s units; nothing is read from the file’s data section. A sweep whose epochs overrun it is refused on read (exit 6).

Normalized fields

TraceInfo: name = protocol name (file stem of protocol_path), sample_rate_hz, sample_count (longest sweep), sweep_count, channels[] (name or ch<i> when blank, unit, dtype int16/float32, scale, offset, extra: adc_number, programmable_gain, instrument_scale, instrument_offset, signal_gain, signal_offset, lowpass_hz, highpass_hz, telegraph {enabled, instrument_code, additional_gain, filter_hz, membrane_capacitance, clamp_mode_code}), start_s = 0. Trace extra: abf_version, generation (abf1/abf2), acquisition_mode (+_code), sample_format, sample_interval_us, adc_range_v, adc_resolution, sweep_interval_s, sweep_sample_counts, sweep_starts_s, created_at, creator, creator_version, protocol, protocol_path, comment, guid, experiment_kind_code, digitizer_code, tags[] {time_s, comment, kind_code}, outputs[] {index, name, unit, holding_level, waveform_enabled, waveform_source_code, stimulus_file, epochs[] {index, kind, kind_code, level, level_step, duration, duration_step, pulse_period, pulse_width}}, digital_outputs[] {epoch, pattern}.

check finding codes

truncated, section_out_of_bounds, bad_offset, bad_section, bad_sample_interval, bad_scaling (ADC range/resolution) (errors); sweep_length_mismatch, synch_length_mismatch, partial_sample_group, data_length_mismatch, no_samples, bad_scaling (per channel), bad_channel_map, tag_outside_recording (warnings); no_creation_time, synch_count_mismatch, unnamed_channel (info). A header that cannot be parsed at all (no signature, no section map, no protocol/ADC/data section, impossible channel count) fails open with a corrupt-file error (exit 4).

Observed corpus values

file version mode format ch sweeps rate (Hz) notes
pyabf-130618-1-12 1.30 episodic int16 1 3 50000 2048-byte header, date 180618
pyabf-invaliddate-abf1 1.30 episodic int16 1 50 20000 date and time −1; blank channel name
pyabf-sample-trace-0054 1.65 gap-free int16 1 1 50000 header says 1081 sweeps (acquisition chunks)
pyabf-file-axon-3 1.83 episodic int16 2 5 20000 sampling sequence 5, 7 (physical ADCs)
pyabf-multichannelabf1withtags 1.84 episodic int16 2 187 20000 2 tags
pyabf-14o16001-vc-pair-step 2.0.0.0 episodic int16 2 13 10000 synch unit 10 µs, sweeps 4 s apart
pyabf-171117-hfmixfret 2.0.0.0 episodic int16 4 13 10000 unit µA (0xB5), user list string
pyabf-user-list-durations 2.0.0.0 episodic float32 2 3 2000 ADC range 10.24 V
pyabf-file-axon-7 2.6.0.0 episodic float32 1 12 403.2258 interval 2480 µs
pyabf-2020-06-16-0000 2.3.0.0 event, variable int16 1 3 10000 sweeps of 3540, 70040, 16040 samples
pyabf-18425108 2.9.0.0 episodic (1 sweep) int16 2 1 25000 same recording as pyabf-18425108-abf1

ATF (Axon Text File; AtfReader, AtfDataset, format id atf, ATF_FORMAT_ID)

Derived from pyABF’s ATF class (MIT, read as documentation) and four ATF 1.0 files from the pyABF repository. Clampfit exports a recording as text:

line content our name
1 ATF and a version (1.0) (looks_like_atf: definite) version
2 number of header records, number of columns (≤ MAX_HEADER_RECORDS, MAX_COLUMNS)
3 … one header record per line: "Key=value", or "Signals=" followed by one tab-separated quoted signal per data column header (acquisition_mode, comment, sweep_start_times_ms, signals_exported, sync_time_units in trace extra)
next column titles: "Time (s)", then "Trace #1 (pA)", … time_title, time_scale (s, ms or µs), AtfColumn (title, channel, sweep, unit from the parentheses)
rest one row per sample: time, then every data column (tab-separated decimals) rows (byte offset per row), times

One trace: channels are the signals in the order Signals= first names them (channels), and the n-th column naming a signal is its n-th sweep (sweeps); without Signals= every column is a channel of one sweep. Rate = 1 / (second time − first time); start_s = first time. Values are the decimal text parsed as float64 (dtype float64, scale 1). Files up to MAX_ATF_LEN. A table whose first column is not time (GenePix results files are also ATF) exits 6. check: truncated (a row with fewer values than columns, or a last row without its line end) (errors); uneven_sweeps, irregular_time, no_samples (warnings); no_signals (info).

file channels sweeps rows rate (Hz)
pyabf-18702001-step-atf 2 (IN 0 pA, IN 1 A) 3 20000 20000
pyabf-model-vc-ramp-atf 1 50 2400 20000
pyabf-model-vc-step-atf 1 20 10000 20000
pyabf-sine-sweep-magnitude-20-atf 1 (no unit) 1 100000 10000

Vocabulary (every public identifier in openreadout-abf must appear here)

identifier meaning
AtfReader, AtfDataset, ATF_FORMAT_ID, MAX_ATF_LEN, MAX_HEADER_RECORDS, MAX_COLUMNS, looks_like_atf, AtfColumn, title, channel, sweep, unit, header, time_title, time_scale, columns, channels, sweeps, rows, times Axon Text File
AbfReader, AbfDataset, FORMAT_ID, open, header, trace_info, MAX_READ_SAMPLES entry points; header returns the parsed AbfFile
AbfFile, generation, version, file_len, header_len, mode, sample_format, data_offset, total_samples, sample_interval_us, sample_rate_hz, header_sweep_count, sweeps, channels, outputs, digital, adc_range_v, adc_resolution, synch_time_unit_us, sweep_interval_s, creator, creator_version, protocol_path, comment, created_at, guid, experiment_kind_code, digitizer_code, tags, synch, sections, strings, findings parsed header
samples_per_channel, data_len, protocol_name, max_sweep_len, variable_length derived values
Generation { Abf1, Abf2 }, AcquisitionMode { EventVariableLength, EventFixedLength, GapFree, HighSpeedOscilloscope, Episodic, Other }, SampleFormat { Int16, Float32 }, from_code, code, name, width, dtype enumerations
SectionEntry, block, entry_size, entry_count, offset, byte_len, end, SECTION_NAMES, SECTION_MAP_OFFSET, SECTION_ENTRY_LEN, BLOCK_LEN, ABF1_BASIC_LEN, ABF1_EXTENDED_LEN, MAX_RECORDS, MAX_STRINGS_LEN layout
InputChannel, index, adc_number, unit, programmable_gain, instrument_scale, instrument_offset, signal_gain, signal_offset, lowpass_hz, highpass_hz, telegraph, scale input channels
Telegraph, enabled, instrument_code, additional_gain, filter_hz, membrane_capacitance, clamp_mode_code amplifier telegraphs
OutputChannel, holding_level, waveform_enabled, waveform_source_code, stimulus_file, epochs, inter_episode_last, conditioning command outputs
CommandPlan, outputs, refused, command_plan, command_sweep, command_trace_info, user_list_active, alternate_dac_output synthesized command waveforms
Epoch, kind, kind_code, level, level_step, duration, duration_step, pulse_period, pulse_width, EpochKind { Off, Step, Ramp, PulseTrain, TriangleTrain, CosineTrain, BiphasicTrain, Other } epoch tables
DigitalEpoch, epoch, pattern digital outputs
Tag, raw_time, time_s tags
Sweep, first_sample, sample_count, start_s sweep layout
channel_scaling, usable_factor, iso_datetime, guid_text, parse_string_table helpers
Block, origin, bytes, read_block, latin1_field, i16_at, u16_at, i32_at, u32_at, i64_at, f32_at, u8_at, bytes_at, text_at bounds-checked byte access

Byte-source integration

The existing layouts are read through Input/Fs/SourceFile, including directory sessions and companions where applicable. Path, memory and callback namespaces use the same parser and scaling. See byte sources.


How this reader was derived, file by file: provenance log.