Skip to content

Matlab

Script for parsing CSI

parseFeitCSI.m
%{
Function which parse FeitCSI data.
usage: parseFeitCSI("path/to/FeitCSI/file")
returned data structure sample:
[
    {
        header: {
            "csi_size":416,
            "ftm_clock":144084200,
            "timestamp":1701251234567890,
            "num_rx":2,
            "num_tx":1,
            "num_subcarriers":52,
            "rssi1":67,
            "rssi2":68,
            "source_mac":(0, 7, 14, 124, 232, 48),
            "source_mac_string":"00:07:0e:7c:e8:30",
            "rate_flags":49415,
            "rate_format":"LEGACY_OFDM",
            "channel_width":"20",
            "mcs":7,
            "antenna_a":true,
            "antenna_b":true,
            "ldpc":false,
            "ss":1,
            "beamforming":false
        },
        data: complex matrix[num_subcarriers, num_rx, num_tx]
    },
    {
        header: ...,
        data: ...
    }, 
    ...
]
%}

function output = parseFeitCSI(fileName)
    RATE_MCS_MOD_TYPE_POS = uint32(8);
    RATE_MCS_MOD_TYPE_MSK = bitshift(uint32(0x7), RATE_MCS_MOD_TYPE_POS);
    RATE_MCS_CCK_MSK = bitshift(uint32(0), RATE_MCS_MOD_TYPE_POS);
    RATE_MCS_LEGACY_OFDM_MSK = bitshift(uint32(1), RATE_MCS_MOD_TYPE_POS);
    RATE_MCS_HT_MSK = bitshift(uint32(2), RATE_MCS_MOD_TYPE_POS);
    RATE_MCS_VHT_MSK = bitshift(uint32(3), RATE_MCS_MOD_TYPE_POS);
    RATE_MCS_HE_MSK = bitshift(uint32(4), RATE_MCS_MOD_TYPE_POS);
    RATE_MCS_EHT_MSK = bitshift(uint32(5), RATE_MCS_MOD_TYPE_POS);
    RATE_MCS_CHAN_WIDTH_POS = uint32(11);
    RATE_MCS_CHAN_WIDTH_MSK = bitshift(uint32(0x7), RATE_MCS_CHAN_WIDTH_POS);
    RATE_MCS_CHAN_WIDTH_20_VAL = uint32(0);
    RATE_MCS_CHAN_WIDTH_20 = bitshift(RATE_MCS_CHAN_WIDTH_20_VAL, RATE_MCS_CHAN_WIDTH_POS);
    RATE_MCS_CHAN_WIDTH_40_VAL = uint32(1);
    RATE_MCS_CHAN_WIDTH_40 = bitshift(RATE_MCS_CHAN_WIDTH_40_VAL, RATE_MCS_CHAN_WIDTH_POS);
    RATE_MCS_CHAN_WIDTH_80_VAL = uint32(2);
    RATE_MCS_CHAN_WIDTH_80 = bitshift(RATE_MCS_CHAN_WIDTH_80_VAL, RATE_MCS_CHAN_WIDTH_POS);
    RATE_MCS_CHAN_WIDTH_160_VAL = uint32(3);
    RATE_MCS_CHAN_WIDTH_160 = bitshift(RATE_MCS_CHAN_WIDTH_160_VAL, RATE_MCS_CHAN_WIDTH_POS);
    RATE_MCS_CHAN_WIDTH_320_VAL = uint32(4);
    RATE_MCS_CHAN_WIDTH_320 = bitshift(RATE_MCS_CHAN_WIDTH_320_VAL, RATE_MCS_CHAN_WIDTH_POS);
    RATE_HT_MCS_CODE_MSK = uint32(7);
    RATE_MCS_ANT_A_POS = uint32(14);
    RATE_MCS_ANT_A_MSK = bitshift(uint32(1), RATE_MCS_ANT_A_POS);
    RATE_MCS_ANT_B_POS = uint32(15);
    RATE_MCS_ANT_B_MSK = bitshift(uint32(1), RATE_MCS_ANT_B_POS);
    RATE_MCS_LDPC_POS = uint32(16);
    RATE_MCS_LDPC_MSK = bitshift(uint32(1), RATE_MCS_LDPC_POS);
    RATE_MCS_SS_POS = uint32(16);
    RATE_MCS_SS_MSK = bitshift(uint32(1), RATE_MCS_SS_POS);
    RATE_MCS_BEAMF_POS = uint32(16);
    RATE_MCS_BEAMF_MSK = bitshift(uint32(1), RATE_MCS_BEAMF_POS);

    function header = parseHeader(data)
        header = {};
        header.csi_size = typecast(data(1:4), 'uint32');
        header.ftm_clock = typecast(data(9:12), 'uint32');
        header.timestamp = typecast(data(13:20), 'uint64');
        header.num_rx = data(47);
        header.num_tx = data(48);
        header.num_subcarriers = typecast(data(53:56), 'uint32');
        header.rssi1 = typecast(data(61:64), 'uint32');
        header.rssi2 = typecast(data(65:68), 'uint32');
        header.source_mac = data(69:74);
        header.source_mac_string = sprintf('%02x:%02x:%02x:%02x:%02x:%02x', header.source_mac);
        header.rate_flags = typecast(data(93:96), 'uint32');

        rate_format = bitand(header.rate_flags, RATE_MCS_MOD_TYPE_MSK);
        if rate_format == RATE_MCS_CCK_MSK
            rate_format = 'CCK';
        elseif rate_format == RATE_MCS_LEGACY_OFDM_MSK
            rate_format = 'LEGACY_OFDM';
        elseif rate_format == RATE_MCS_VHT_MSK
            rate_format = 'VHT';
        elseif rate_format == RATE_MCS_HT_MSK
            rate_format = 'HT';
        elseif rate_format == RATE_MCS_HE_MSK
            rate_format = 'HE';
        elseif rate_format == RATE_MCS_EHT_MSK
            rate_format = 'EHT';
        else
            rate_format = 'unknown';
        end
        header.rate_format = rate_format;

        channel_width = bitand(header.rate_flags, RATE_MCS_CHAN_WIDTH_MSK);
        if channel_width == RATE_MCS_CHAN_WIDTH_20
            channel_width = '20';
        elseif channel_width == RATE_MCS_CHAN_WIDTH_40
            channel_width = '40';
        elseif channel_width == RATE_MCS_CHAN_WIDTH_80
            channel_width = '80';
        elseif channel_width == RATE_MCS_CHAN_WIDTH_160
            channel_width = '160';
        elseif channel_width == RATE_MCS_CHAN_WIDTH_320
            channel_width = '320';
        else
            channel_width = 'unknown';
        end
        header.channel_width = channel_width;

        header.mcs = bitand(header.rate_flags, RATE_HT_MCS_CODE_MSK);

        if bitand(header.rate_flags, RATE_MCS_ANT_A_MSK)
            header.antenna_a = true;
        else    
            header.antenna_a = false;
        end

        if bitand(header.rate_flags, RATE_MCS_ANT_B_MSK)
            header.antenna_b = true;
        else    
            header.antenna_b = false;
        end

        if bitand(header.rate_flags, RATE_MCS_LDPC_MSK)
            header.ldpc = true;
        else    
            header.ldpc = false;
        end

        if bitand(header.rate_flags, RATE_MCS_SS_MSK)
            header.ss = 2;
        else    
            header.ss = 1;
        end

        if bitand(header.rate_flags, RATE_MCS_BEAMF_MSK)
            header.beamforming = true;
        else    
            header.beamforming = false;
        end

    end


    function csi_matrix = parseCsiData(data, header)
        csi_matrix = zeros(header.num_subcarriers, header.num_rx, header.num_tx);
        csi_matrix  = complex(csi_matrix ,0);
        pos = 1;
        for j = 1:header.num_rx
            for k = 1:header.num_tx
                for n = 1:header.num_subcarriers
                    real = typecast(data(pos:pos + 1), 'int16');
                    imag = typecast(data(pos + 2:pos + 3), 'int16');
                    pos = pos + 4;
                    csi_matrix(n, j, k) = complex(real, imag);
                end
            end
        end
    end

    f = fopen(fileName);
    fileContent = uint8(fread(f));
    step = 1;
    index = 1;
    while length(fileContent) > step
        data = {};
        data.header = parseHeader(fileContent(step:step + 271));
        step = step + 272;
        data.csi_matrix = parseCsiData(fileContent(step:step+data.header.csi_size - 1), data.header);
        step = step + data.header.csi_size;
        output(index ) = data;
        index  = index  + 1;
    end
end

Script for parsing FTM

parseFeitCSIftm.m
%{
Function which parse FeitCSI FTM data.
usage: parseFeitCSIftm("path/to/FeitCSI/ftm/file")
returned data structure sample:
[
    {
        "burstIndex": 123,
        "numFtmrAttempts": 123,
        "numFtmrSuccesses": 123,
        "numBurstsExp": 123,
        "burstDuration": 123,
        "ftmsPerBurst": 123,
        "rssiAvg": 123,
        "rssiSpread": 123,
        "rttAvg": 123,
        "rttVariance": 123,
        "rttSpread": 123,
        "distAvg": 123,
        "distVariance": 123,
        "distSpread": 123,
        "timestamp": 123,
    },
    {
        FTM data...
    }, 
    ...
]
%}

function output = parseFeitCSIftm(fileName)

    function ftmData = parseFtmData(data)
        ftmData = {};
        ftmData.burstIndex = typecast(data(1:4), 'uint32');
        ftmData.numFtmrAttempts = typecast(data(5:8), 'uint32');
        ftmData.numFtmrSuccesses = typecast(data(9:12), 'uint32');
        ftmData.numBurstsExp = typecast(data(13:16), 'uint32');
        ftmData.burstDuration = typecast(data(17), 'uint8');
        ftmData.ftmsPerBurst = typecast(data(18), 'uint8');
        ftmData.rssiAvg = typecast(data(19), 'uint8');
        ftmData.rssiSpread = typecast(data(20:23), 'uint32');
        ftmData.rttAvg = typecast(data(24:31), 'uint64');
        ftmData.rttVariance = typecast(data(32:39), 'uint64');
        ftmData.rttSpread = typecast(data(40:47), 'uint64');
        ftmData.distAvg = typecast(data(48:55), 'uint64');
        ftmData.distVariance = typecast(data(56:63), 'uint64');
        ftmData.distSpread = typecast(data(64:71), 'uint64');
        ftmData.timestamp = typecast(data(72:79), 'uint64');
    end

    f = fopen(fileName);
    fileContent = uint8(fread(f));
    step = 1;
    index = 1;
    while length(fileContent) > step
        ftmData = parseFtmData(fileContent(step:step + 78));
        step = step + 79;
        output(index) = ftmData;
        index  = index  + 1;
    end
end