Read a .bin file generated by the IRIS Instruments Syscal Pro System

    Parameters
    ----------
    filename : string
        Path to input filename

    Returns
    -------
    metadata : dict
        General information on the measurement
    df : :py:class:`pandas.DataFrame`
        dataframe containing all measurement data

def _import_bin(filename):
    """Read a .bin file generated by the IRIS Instruments Syscal Pro System

    Parameters
    ----------
    filename : string
        Path to input filename

    Returns
    -------
    metadata : dict
        General information on the measurement
    df : :py:class:`pandas.DataFrame`
        dataframe containing all measurement data

    """
    fid = open(filename, 'rb')

    def fget(fid, fmt, tsize):
        buffer = fid.read(tsize)
        result_raw = struct.unpack(fmt, buffer)
        if len(result_raw) == 1:
            return result_raw[0]
        else:
            return result_raw

    # determine overall size
    fid.seek(0, 2)
    total_size = fid.tell()
    # print('total size', total_size)

    # start from the beginning
    fid.seek(0)

    # read version
    buffer = fid.read(4)
    version = struct.unpack('I', buffer)
    # print('version', version)
    buffer = fid.read(1)
    typedesyscal = struct.unpack('c', buffer)[0]
    syscal_type = int.from_bytes(typedesyscal, 'big')
    # print('Syscal type: {}'.format(syscal_type))

    # comment
    buffer = fid.read(1024)
    comment_raw = struct.iter_unpack('c', buffer)
    comment = ''.join([x[0].decode('utf-8') for x in comment_raw])
    # print('comment', comment)

    metadata = {
        'version': version,
        'syscal_type': syscal_type,
        'comment': comment,
    }

    measurements = []
    # for each measurement
    counter = 0
    while(fid.tell() < total_size):
        # print('COUNTER', counter)
        buffer = fid.read(2)
        array_type = struct.unpack('h', buffer)
        array_type
        # print(array_type)
        # not used
        moretmeasure = fget(fid, 'h', 2)
        moretmeasure
        # print('moretmeasure', moretmeasure)
        # measurement time [ms]
        mtime = fget(fid, 'f', 4)
        # print('measurement time', mtime)
        # delay before IP measurements start [ms]
        mdelay = fget(fid, 'f', 4)
        # print('mdelay', mdelay)
        TypeCpXyz = fget(fid, 'h', 2)
        # our file format documentation always assumes this value to be == 1
        assert TypeCpXyz == 1
        # print('TypeCpXyz', TypeCpXyz)
        # ignore
        fget(fid, 'h', 2)
        # positions: a b m n [m]
        xpos = fget(fid, '4f', 16)
        # print('xpos', xpos)
        ypos = fget(fid, '4f', 16)
        # print('ypos', ypos)
        zpos = fget(fid, '4f', 16)
        # print('zpos', zpos)
        # self-potential [mV]
        sp = fget(fid, 'f', 4)
        # print('sp', sp)
        # measured voltage at voltage electrodes m and n [mV]
        vp = fget(fid, 'f', 4)
        # print('vp', vp)
        Iab = fget(fid, 'f', 4)
        # print('iab', Iab)
        rho = fget(fid, 'f', 4)
        # print('rho', rho)
        m = fget(fid, 'f', 4)
        # print('m', m)
        # standard deviation
        q = fget(fid, 'f', 4)
        # print('q', q)
        # timing windows
        Tm = fget(fid, '20f', 20 * 4)
        Tm = np.array(Tm)
        # print('Tm', Tm)
        # chargeabilities
        Mx = fget(fid, '20f', 20 * 4)
        Mx = np.array(Mx)
        # print('Mx', Mx)
        # this is 4 bytes used to store information on the measured channel
        # Channel + NbChannel
        buffer = fid.read(1)
        buffer_bin = bin(ord(buffer))[2:].rjust(8, '0')
        # print(buffer_bin)
        channel = int(buffer_bin[4:], 2)
        channelnb = int(buffer_bin[0:4], 2)
        # print('ChannelNB:', channelnb)
        # print('Channel:', channel)
        # 4 binaries + unused
        buffer = fid.read(1)
        buffer_bin = bin(ord(buffer))[2:].rjust(8, '0')
        # print(buffer_bin)
        overload = bool(int(buffer_bin[4]))
        channel_valid = bool(int(buffer_bin[5]))
        channel_sync = bool(int(buffer_bin[6]))
        gap_filler = bool(int(buffer_bin[7]))
        # print(overload, channel_valid, channel_sync, gap_filler)
        measurement_num = fget(fid, 'H', 2)
        # print('measurement_num', measurement_num)
        filename = fget(fid, '12s', 12)
        # print('filename', filename)
        latitude = fget(fid, 'f', 4)
        # print('lat', latitude)
        longitude = fget(fid, 'f', 4)
        # print('long', longitude)
        # number of stacks
        NbCren = fget(fid, 'f', 4)
        # print('Stacks', NbCren)
        # RS check
        RsChk = fget(fid, 'f', 4)
        # print('RsChk', RsChk)
        # absolute applied voltage
        vab = fget(fid, 'f', 4)
        # print('Vab', vab)
        # tx battery voltage [V]
        batTX = fget(fid, 'f', 4)
        # print('batTX', batTX)
        # rx battery voltage [V]
        batRX = fget(fid, 'f', 4)
        # print('batRX', batRX)
        temperature = fget(fid, 'f', 4)
        # print('Temp.', temperature)
        # TODO: date and time not analyzed
        b = struct.unpack('2f', fid.read(2 * 4))
        # print('b', b)
        b

        measurements.append({
            'version': version,
            'mtime': mtime,
            'x_a': xpos[0],
            'x_b': xpos[1],
            'x_m': xpos[2],
            'x_n': xpos[3],
            'y_a': ypos[0],
            'y_b': ypos[1],
            'y_m': ypos[2],
            'y_n': ypos[3],
            'z_a': zpos[0],
            'z_b': zpos[1],
            'z_m': zpos[2],
            'z_n': zpos[3],
            'mdelay': mdelay,
            'vp': vp,
            'q': q,
            'overload': overload,
            'channel_valid': channel_valid,
            'channel_sync': channel_sync,
            'gap_filler': gap_filler,
            'NbStacks': NbCren,
            'm': m,
            'Tm': Tm,
            'Mx': Mx,
            'nr': measurement_num,
            'vab': vab,
            'channel': channel,
            'sp': sp,
            'Iab': Iab,
            'rho': rho,
            'latitude': latitude,
            'longitude': longitude,
            'channelnb': channelnb,
            'RsCHk': RsChk,
            'batTX': batTX,
            'batRX': batRX,
            'temperature': temperature,
            'measurement_num': measurement_num,
        })

        counter += 1

    # create a dataframe with all primary data
    df = pd.DataFrame(
        measurements
    ).reset_index()
    return metadata, df