The binary file format summarized in this support article pertains to binary files exported via exporting raw data described in the support article linked below.
If you were looking into converting your .sal capture file into the binary file format described in the support article, you can refer to the support article linked below.
In Logic 2, both digital and analog binary files start with the same header, which can be used to determine the type of data stored in the file.
All multi-byte values are in little endian byte-order
Floating point values are IEE754
Digital and analog binary files have the same initial header that can be used to determine whether it is a valid Saleae binary export file, the version, and whether it contains analog or digital data.
byte[8] identifier;
int32 version;
int32 type;
For Saleae binary exports, the identifier will always be <SALEAE>. The current version is 0.
Types:
0 - Digital
1 - Analog
The remaining format is based on the type of the data.
Digital
File structure (starting with the shared header above)
byte[8] identifier;
int32 version;
int32 type;
uint32 initial_state;
double begin_time;
double end_time;
uint64 num_transitions;
for each transition in num_transitions
double transition_time;
Analog
File structure (starting with the shared header above)
byte[8] identifier;
int32 version;
int32 type;
double begin_time;
uint64 sample_rate;
uint64 downsample;
uint64 num_samples;
for each sample in num_samples:
float voltage;
Sample Python code for reading binary data
Digital data
import arrayimport structimport sysfrom collections import namedtupleTYPE_DIGITAL =0TYPE_ANALOG =1expected_version =0DigitalData = namedtuple('DigitalData', ('initial_state', 'begin_time', 'end_time', 'num_transitions', 'transition_times'))
defparse_digital(f):# Parse header identifier = f.read(8)if identifier !=b"<SALEAE>":raiseException("Not a saleae file") version, datatype = struct.unpack('=ii', f.read(8))if version != expected_version or datatype != TYPE_DIGITAL:raiseException("Unexpected data type: {}".format(datatype))# Parse digital-specific data initial_state, begin_time, end_time, num_transitions = struct.unpack('=iddq', f.read(28))# Parse transition times transition_times = array.array('d') transition_times.fromfile(f, num_transitions)returnDigitalData(initial_state, begin_time, end_time, num_transitions, transition_times)if__name__=='__main__': filename = sys.argv[1]print("Opening "+ filename)withopen(filename, 'rb')as f: data =parse_digital(f)# Print out all digital data initial_state_str ='low'if data.initial_state ==0else'high'print('Initial state: '+ initial_state_str)print('Begin time: {0:.6f}'.format(data.begin_time))print('End time: {0:.6f}'.format(data.end_time))print("Num transitions: {}".format(data.num_transitions)) cur_state = data.initial_stateprint(" {0:>20}{1}".format("Time", "Bit State"))print(" {0:>20.6f}{1}".format(data.begin_time, 'low'if cur_state ==0else'high'))for time in data.transition_times:# This is a transition, flip the bit state cur_state =0if cur_state else1print(" {0:>20.6f}{1}".format(time, 'low'if cur_state ==0else'high'))
Example output:
Opening binary4/digital_5.bin
Identifier: <SALEAE>
Version: 0
Datatype: 0
Initial state: low
Begin time: 0.000000
End time: 0.033341
Num transitions: 1031
Time Bit State
0.000000 low
0.000009 high
0.000028 low
0.000037 high
0.000099 low
0.000139 high
0.000168 low
0.000171 high
0.000228 low
0.000289 high
0.000332 low
...
Analog data
import arrayimport structimport sysfrom collections import namedtupleTYPE_DIGITAL =0TYPE_ANALOG =1expected_version =0AnalogData =namedtuple('AnalogData', ('begin_time', 'sample_rate', 'downsample', 'num_samples', 'samples'))defparse_analog(f):# Parse header identifier = f.read(8)if identifier !=b"<SALEAE>":raiseException("Not a saleae file") version, datatype = struct.unpack('=ii', f.read(8))if version != expected_version or datatype != TYPE_ANALOG:raiseException("Unexpected data type: {}".format(datatype))# Parse analog-specific data begin_time, sample_rate, downsample, num_samples = struct.unpack('=dqqq', f.read(32))# Parse samples samples = array.array("f") samples.fromfile(f, num_samples)returnAnalogData(begin_time, sample_rate, downsample, num_samples, samples)if__name__=='__main__': filename = sys.argv[1]print("Opening "+ filename)withopen(filename, 'rb')as f: data =parse_analog(f)# Print out all analog dataprint("Begin time: {}".format(data.begin_time))print("Sample rate: {}".format(data.sample_rate))print("Downsample: {}".format(data.downsample))print("Number of samples: {}".format(data.num_samples))print(" {0:>20}{1:>10}".format("Time", "Voltage"))for idx, voltage inenumerate(data.samples): sample_num = idx * data.downsample time = data.begin_time + (float(sample_num)/ data.sample_rate)print(" {0:>20.10f}{1:>10.3f}".format(time, voltage))
Example output:
Opening binary_export/analog_5.bin
Identifier: <SALEAE>
Version: 0
Datatype: 1
Begin time: 0.0
Sample rate: 50000000
Downsample: 1
Number of samples: 1667072
Time Voltage
0.0000000000 -0.002
0.0000000200 0.784
0.0000000400 1.560
0.0000000600 2.332
0.0000000800 3.089
0.0000001000 3.827
0.0000001200 4.540
0.0000001400 5.223
0.0000001600 5.873
0.0000001800 6.493
...
Community Shared Binary Parser
A community user has generously shared their C-based binary parser for digital channels! Their GitHub repository for it can be found here: saleae-binparser
Logic 1.x
If you are using Logic 1.x, please refer to the articles below.