Contec KT88-2400 - 24 channel EEG

Making & changing hardware drivers in Acquisition Server
Post Reply
a1eks
Posts: 18
Joined: Sun Jan 21, 2018 6:26 pm

Contec KT88-2400 - 24 channel EEG

Post by a1eks »

Hi,

I know there were topics about drivers for Contec devices, especially for the KT88-1600 (16ch EEG), which worked well (the source code in the attachment), however, there was nothing for the KT88-2400, and so I did some reverse engineering to figure out how to decode the commands. Here are my findings.

I will publish here the communication protocol, and it might be useful for somebody who is interested to contribute to the driver development.

Acquisition and filter control (commands in hex format)
90 01 Start Acquisition
90 02 Stop acquisition
90 03 Enable HW filter 0.5-35Hz
90 04 Disable HW filter
90 05 Start impedance measurement
90 06 Disable impedance measurement

Setting physical reference electrode
91 01 AA reference (left hemisphere referenced to the left ear lobe, right to the right earlobe)
91 02 A1 reference (all electrodes referenced to the A1)
91 03 A2 reference
91 04 AVG (average reference)
91 05 Cz reference
91 06 BN (balanced noncephalic reference)

Montage setup
92 0X (9 defined montages X=1,...,9, the montage can be explored by going to System configuration -> montage ways). Changes of default montage always follow the command 91 04 (avg reference).


Uknown commands
80 00
81 00

Example of the default system settings sequence sent by the provided EEG software.

90 02 //stop acquisition
80 00 //?
81 00 //?
91 01 //set AA reference
90 09 //?
90 03 //Enable HW filter
90 06 //Disable impedance reading
90 01 //Start data acquisition
... DATA STREAM ...
90 02 //Stop acquisition

Data Stream


Baud rate 921600 (has to be that one)
Data sampling rate 200Hz
Encoding bits 12
Number of channels 26

Stream format:

After executing (sending the command to the amplifier) 90 01, the data stream starts with two bytes e0 01 which indicate the start of the data streaming and it is followed by 46 bytes that carry the data for 26 channels. Each block of 46 bytes starts with a0 0 followed by 4 most significant bits of the channel number 1. The rest 8 bits (12bits total -4 =8) are taken from the consecutive byte. All consecutive bytes carry information only in 7 least significant bits (because the first bit is always reserved for the start of the sequence and cannot be used). To make this more illustrative I will try to represent it graphically below. Suppose that we have started the amplifier and it always gives us 4096 for all channels (so we have all "ones" for the data), then we have something like this:

2bytes
1110 0000 0000 0001

46bytes
1010 0000 0000 1111 0111 1111 0111 1111 0111 1111 0111 1111 0111 1 ...



* - data stream start

* -block start

* - channel 1 first sample

* - channel 2 first sample

* - channel 3 first sample
...
until channel 26.



In hex format, 3 samples

e0 01
a0 0f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f a0 0f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f a0 0f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f...


Now I would like to modify the code in the attachment (driver for KT88-1600) and I am struggling with the method CDriverContecKT88::getPacket, so I need help with how to properly shift bits, so I extract 12 bits of data and ignore the first bit. Maybe the problem is trivial, but I don't have much experience with bit shifts.

Thank you in advance!

EDIT: drivers source https://github.com/miladinovic/KT88-160 ... ibe-driver

Thomas
Posts: 74
Joined: Wed Mar 04, 2020 3:38 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by Thomas »

Hi A1eks,

I had a quick look online, and I see that the KT88 2400 is a 24 channels device, and not 26. (Are the two extra ground and reference?)

Also, any bit shifts and masking is possible, but I am a bit skeptical about the format you describe, notably about the 12 bits shifting across bytes.
How did you get the information about the reserved bit at the beginning of each sequence ?

Sorry for asking more questions rather than bringing answers.

Cheers,
Thomas

a1eks
Posts: 18
Joined: Sun Jan 21, 2018 6:26 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by a1eks »

Hi Thomas,

Thanks for the help. As I have KT88-2400 I can confirm that there are 26channels available, and these two additional are PG1 and PG2 (nasopharyngeal electrodes), nowadays rarely used. In a certain type of montage setting, they do appear though in the official software.
How did you get the information about the reserved bit at the beginning of each sequence?
The data format is a nightmare, it took me weeks to figure it out. Finally, I did some calculations and then everything fitted perfectly, so I am pretty confident about it. The reserved bit has to be there because of the "0xA0" marker, and I confirmed it by spoofing serial communication and saturating amplifier, so I obtained a series of 7fs in 45bytes chunks.

But, that is not the end, I managed to implement bit shifting and masking in python, but then I realized that it is even worse than I thought. As in KT88-1600, the two consecutive bytes have to be swapped, to make it work.
// Each channel value is formed by two consecutive bytes, the first byte is
// the least significant one and the second the most significant one. That means
// we need to write both bytes into a 16 bit unsigned integer and swap its byte
// order
Comment from KT88-1600 driver https://github.com/miladinovic/KT88-160 ... ecKT88.cpp


This is the provisional decoding script I made tonight. It contains also a stream of data registered from the amplifier. The ECG is clearly visible on the last channel, but I don't know about the other channels if it decodes it properly. I will check it out tomorrow.



Image

Code: Select all

import io

file_object = open('/tmp/eeg.txt', 'w+')



def swap_bytes(b):
    bt=bytearray(b)
    a=bt[0]<<4 | (bt[1] & 0xF)
    bt[0]=bt[1]>>4
    bt[1]=a
    for i in range(2,44,2):
        a=bt[i]
        bt[i]=bt[i+1]
        bt[i+1]=a
    return  int.from_bytes(bt, byteorder='big')


def remove_bit(num, i):
    mask = num >> (i + 1)
    mask = mask << i
    right = ((1 << i) - 1) & num
    return mask | right

def insert_mult_bits(num, bits, len, i):
    mask = num >> i
    mask = (mask << len) | bits
    mask = mask << i
    right = ((1 << i) - 1) & num
    return right | mask


def decode_data(b):
    sample=swap_bytes(b)

    #remove unused bits
    corr=0;
    for i in range(7, sample.bit_length(), 8):
        sample=remove_bit(sample,i-corr);
        corr=corr+1;

    #add HSB to make 2byte representation
    corr=0;
    for i in range(12,sample.bit_length(),12):
        sample=insert_mult_bits(sample,0,4,i+corr)
        corr=corr+4;

    #convert to bytes 26channels x 2 bytes, bigendian
    bt=sample.to_bytes(26*2,'big');

    #assign the result to int list
    idx=0;
    out=[];
    for i in range(0,26*2-1,2):
        out.append(int(int((bt[i]<<8 | bt[i+1]))))
        idx=idx+1;


    #print the values
    print(','.join(str(item) for item in out))
    file_object.write(','.join(str(item) for item in out)+'\n')



if __name__ == '__main__':



    b=  b'\xa0\x02vBy\x16\x103\x166V\x02J(\x12n\x19/\x0f9:pw\x0c~)\x03M\x18Gn\x08RG\x06|ZX\x7ftzs1\x06]i' \
+ b"\xa0\x06t\x07OT]Wh\x00i=v\x19\x17T\x19\x0cr\nN'y3\x11\t]h\x1a\x13F\x1a7\x1f%[eX\x0c\x7fx\x02+\x07-\x08" \
+ b'\xa0\rJ,\x10y\x1aFX.\x13e63J>3ou#O0s\x1c93m"3wo#$z,j\x13H!(\x07\x03-\x08FM' \
+ b'\xa0\t-Ry]^\x1fv&<\n>\t7x\tq\x06\x18eO*(q\x19D/\x08\n\\)a \x16IWv^s\x05r \t*T' \
+ b'\xa0\x02g\nY\x1f\x103\x03\x0fFFQ)\x0f\r\x19=39\x17\x05hv*)\x17`\x19N\x07\x08\x00b\x06\x179XZmzw;\x06_f' \
+ b'\xa0\x02t&MT\\W^}i%z\x19<d\x1a!\x02\n\x1d+id0\x19\x0ed\x1a\x15C\x1a\\)%fdY\x06\x03x\x04/\x07/\x0c' \
+ b'\xa0\rJ\x0c\x10y\x1cFoG\x13\\\x163R53du#\r2s\x7f93h\x143[g#,w,h&H#0w~*\x08FM' \
+ b'\xa0\t-\x13i]^\x1fbQ<\x17+\t\x14`\x089r\x187R+*\x00\x19)%\x08eP)G\x1b\x16Ccvs~\x05v\x11\t+W' \
+ b'\xa0\x02\x7ff{V\x103D1Fz"\x18cY\x19\x0c\x02)uWg\x0fr\x19t2\x18(W\x08K1\x06j{X\x17szu8\x06]i' \
+ b'\xa0\x06v\x06M\x14]Wn>iq\x07\x194g\x1a%\x18\n=Cy\x13Q\x19By\x1aL`\x1a\x7fC%}XXttx\x01,\x07.\x08' \
+ b"\xa0\x0fK,\x02y\x1cF\x03o\x13/I3s>3wy#S+s#z3$,C\x13t#j|,k'H0'w}.\x08FQ" \
+ b"\xa0\t}\x12Y\\^\x1fjR<5u\tk%\t\x1c:\x18tq+-[\x19ko\t*\x0b9\x15M\x16pdv}}\x05n\x10\t'X" \
+ b'\xa0\x02\x7f\x07{V\x103\x01\x0fF46\x18cO\x18\x16|)eThU\x1e\x19R;\x18\t[\x08)/\x06arX\x11vzuC\x06^h' \
+ b'\xa0\x02=#_\x14]W;sYmV\tb8\x19oW\nl\x13i0\x11\t\rN\n41\x1a\x16\x0f%GlX}}x\x089\x07/\t' \
+ b'\xa0\rJ\x0c\x10y\x1aFoH\x13J\x163\x0b\x1939L#\x05\x0bsG\x0c3\x14\x003%Q#$U,J:H>)\x07\x02#\x08EV' \
+ b'\xa0\t-ri\\^\x1fI6<\x1f:\t&z\tF\x02\x18dd\x1axf\x19\x1dM\x08t\x7f9\x072\x16^SvB\x7f\x05w\x0f\t)Z' \
+ b'\xa0\x02\x7fF{\x16\x113\x1c\x0eFf/\x18dd\x19\x07\x03){egc^\x19_H\x18\x17p\x08W@\x06oaXs\x00zsF\x06]n' \
+ b'\xa0\x06v&I\x14]We+i6\x18\x19Dl\x1a\x1e\x1a\nPMiw0\x1a\r\x0c\x1a\x18o\x1avH%wOXh}x\n&\x07,\x0f' \
+ b'\xa0\x0fK,\x10y\x1aF-Z\x13(+3Q+3Zq#?&sR03j$3rn#Es,\\8H<"\x07\x05+\x08FZ' \
+ b'\xa0\t}\x1eY\\V\x1fk<<?Y\tZ\x0f\t\x0c7\x18$\x0b+m\x1c\x19]~\t6\x179\x06P\x16lMvXy\x05p~\t(\\' \
+ b'\xa0\x02>g{F\x113a\x18V\x1f\x03\x18F6\x18qe)r;g)G\x19F\x1b\x18\x0f;\x08(\r\x07<$XR\x04zn:\x06\\j' \
+ b'\xa0\x06v&k\x1c]WO[i%\x1b\x19/x\x1a72\n[hi}\\\n{\x1d\x1a4\x03\x1an^%\x0b<Xexx\x06\x1d\x07-\x0c' \
+ b'\xa0\x0bJ,\x10y\x1cG!\x14\x13f[3:43Ie#I\x18s\x19]3J\x183tX#Ub,M>HJ$w\x7f\x1e\x08F]' \
+ b"\xa0\tv\x1e]\\^\x1fb2<o\x15\x19\x01@\t\x18R\x18[,+]T\x19\x7f\x02\t^%9.q\x16\x7f%v0}\x05g\x10\t'\\" \
+ b'\xa0\x06?c{F\x112_JFq"\x18S6\x18mb9\x00?g"f\x19R\x1b\x18\x0f8\x08 \x16\x07=\x18X>\x05zkI\x06\\j' \
+ b'\xa0\x02v\x06i\x1c]WipiL\x18\x19*t\x1a/-\n\\hy4W\x1a\x15\x13\x1a\x0f\x03\x1ame%\x08BX_yx\r\x11\x07,\n' \
+ b'\xa0\x0fK,\x14y\x1aF6E\x13%B3&\x173FN#4\x07sU<2m}3^H#0U,>GHH#\x07\t\x0b\x08Fa' \
+ b"\xa0\t|\x1e[\\^\x1f8 <[q\t[\x1e\t\x17E\x18\x19)+P8\x19 o\t_*9\x0fm\x16z\x1dv\x1fy\x05o\x06\t'V" \
+ b'\xa0\x06=g{F\x112\\XFAt\x18,#\x18[R)01g\x10E\x19=\x06\x18\x110\x08\x0c\x06\x072$Xe\rzkY\x06\\l' \
+ b'\xa0\x06v\x06i\x1d]WR+i+\x0b\x19Nz\x1aQC\nA|y.h\x1a\x19+\x1a[\x16\x1a\x05q%\x1c2XYqx\x00\x15\x07-\x06' \
+ b'\xa0\x0bK,\x10y\x1cGM\x14\x13*>32\x1c3?S#&\x04sVY3A\x103YN#RR,=XHg\x19w\x7f\t\x08D^' \
+ b"\xa0\t~\x1e]V^\x1f+D<\x7f\x07\tt>\t\x1fe\x18(?+hl\x19M\x1c\t@H9'\x00&\x0f v\x1au\x05t\x06\t'W" \
+ b'\xa0\x06=g+G\x112+hFKp\x186#\x18XW)K6g\x1eW\x19*\x0f\x18k3xw\x08\x07/!X+\x06zxW\x06\\i' \
+ b'\xa0\x06f&i\x1c]W\x0f\x1ei\x00\x07\x1a4\x04\x1a:0\n)gizq\x1a\x0e\x18\x1a\x1d\r\x1akd%\x108X6vx\x0f\x16\x07-\x06' \
+ b'\xa0\x0bK \x10y\x1aG "\x13\x10=3\x11#30N#uss\x1fp3M\x043II#/P,@dHp\x1c\x07\x10\x16\x08Da' \
+ b'\xa0\t~^_V^\x1f\x1aZ<`\x1b\ti]\t\x1dy\x18"/+2\x10\x19\x16$\tPY9"\x15&\'0v@y\x05v\x08\t&W' \
+ b'\xa0\x06?Go\x04\x112||F9\x05\x18"\x18\x18HN)\'\x19gcQ\x18\x1fy\x18f&\x08\x07y\x07 Ph\x1e\x15zg[\x06\\k' \
+ b"\xa0\x06gni\x1f]W\x18[YZ5\x1aT\x1b\x1an]\n>\x1aiR\x1f\x1a\x19S\x1aN@\x1a\x13\x18%9\x00X'yx\x05\x0b\x07,\x07" \
+ b'\xa0\x0bK$\x10y\x1aGC$\x13\t43,\x123AI#\x0bvs\x1fJ3J\x043cC#TB,5`Hl&\x07\x07\x1c\x08E^' \
+ b"\xa0\tt\x1e_T^\x1f-<<ie\x19\x18&\t>X\x18,\x16+?R\x19\x1d\x0e\tv49A[&\x055v\x19x\x05t\x15\t'W" \
+ b'\xa0\x06-\x0f\x0fI\x112~CFX\x1d\x18\'c\x18@!("zg\x7fo\x18"Y\x18j~xgKw}]Xv\x0ezlY\x06[m' \
+ b'\xa0\x06f&i\x1c]W/\x19i\x01\x03\x1ai\x0f\x1aN6\nUoiw;\x1a2\x1d\x1aE\x16\x1a|l%\x1d0X2rx\x17\x12\x07,\x04' \
+ b'\xa0\x0bJ$\x14y\x1aGB\x1d\x13u\x173\x1e\x0c3*=#\x11os\x04,2Bl3O<#>8,4mHx\x1f\x07\r\x0f\x08Ff' \
+ b'\xa0\x0bv\x1f_\x06^\x0fiI<O\x03\x19\x13Q\t]\x0b\x18Yj+\x13{\x19&0\tzy9H\x1f&?|vos\x05p\x05\t%W' \
+ b'\xa0\x04-G/\t\x11B\x02nFRG\x18\x00d\x184!)\x0c\x04g|1\x18\x00V\x18X\x01xcNw\x7f`h\x19\x18zci\x06[l' \
+ b"\xa0\x02gNi\x1f]X2\nYrD\x1af.\x1aob\nY!y)7\x1aFT\x1ag@\x1a#\x12%?\rX'mx\x12\x01\x07-\x03" \
+ b'\xa0\x0bJ$\x149\x1aG[j\x13vs3\x16\x1b3"A#\r_sOt2R~3U-#[4,/vX\x11\x1c\x07\x0f\x1c\x08Ek' \
+ b'\xa0\tv_O\x16^\x1f\x0c=<KO\x19Dp\tr\x19\x18zW+[\x13\x19U9\x19Fb9|&&A\x03v{p\x05g\t\t$W' \
+ b'\xa0\x04%\x0e\r\x19\x11B\x1b4FwP\x18{9\x18$|(\x14`gzl\x18}1\x18daxJ*wS\th(\x16zdd\x06Zn' \
+ b'\xa0\x06.\ny\x1fUW\x18\x0bi+%\x1a\x00%*\x05P\x1a \x18y\x15~\x1aWC\x1a ;\x1a\x0e\x08%3\x0fX\x0efx\x12}\x07.\x01' \
+ b'\xa0\x0bK$\x14)\x1aGm\t\x13\x19\x1d3\x16\x023\x15##*PsPJ2<c3_\x1d#5\x19-\x17\x08X!\x13\x07\x0c\x17\x08Cf' \
+ b'\xa0\tv[_V^\x1f\x0f/<(\x1b\x192Z\tV\x00(\x00Q+7\x12\x19?<\t\x7f]9]\x12&=\x01v\x04o\x05o\x12\t%U' \
+ b"\xa0\x04%\x0e\r\t\x11BL$FI'\x18k9\x18\x0fy(\n_gza\x18l9\x18#[xB/wU{h/\x14z_m\x06Zl" \
+ b'\xa0\x06.\ni\x1f\x15Wa\x08iA(\x1a\x1e4*\x14`\x1a!\x19y@g\x1a\\C\x1a_9\x1a4\x16%?\x08X\x05hx\t{\x07-\x7f' \
+ b'\xa0\tK$\x14)\x1aW,\x13\x13_@31\t3-2#4Rso\x1e2Ib3<$#k%-+\x13X,\x1b\x07\x0e\x11\x08Do' \
+ b'\xa0\x0b&so\x0f^\x0f]!<fV\x1ax(\x198U)02+\x12-\x19e\x7f\x19\x1eN9\x11s&\x1c0v4i\x05s\x05\t!W' \
+ b'\xa0\x04%*\r\x19\x11B\x10\x19FP\x0f\x18e\x1c\x18\x08](lXg,W\x18I)\x18\x08Qx$\x17w=\x12h<!zbu\x06Yn' \
+ b'\xa0\x04.Ni\x0f\x1dWj*i\x0c&\x1a\x109*\x03b\nr.y<$\x1a0Y\x1a F\x1a\x01\x1b%?uX\x03mx\x0f\x06\x07.{' \
+ b'\xa0\x0b\x1b \x04)\x1aGf\x17\x13R42\n\x7f3y\x19#s:sZU2\x0e]3\x7f\x04#\x1a\x0e-\r\x16X?\x13\x07\r\x08\x08Cv' \
+ b'\xa0\x0bfS\x7f\x0e^\x0f2"<[V\x1aO(\tsF)\x06\x19+(D\x192s\tj&9Ui&\x00&v5p\x05n\x0f\t![' \
+ b"\xa0\x04%\n\r\x19\x11B.\x1fFt\x0f\x18Y6\x18\x00{(R^gCk\x18O1\x18\x0b`x\x19*wS\th:'z[i\x06Zr" \
+ b'\xa0\x06.oyNUW\x1e\x07i1,\x1a\x1bY*$\x06\nc5i{\r\x1aIb\x1a\r^\x1au0%\\cHKdx\x14w\x07.\x04' \
+ b'\xa0\t\x13 4"\x1aW\x1d\x1a\x13?\x1f2kr3q\x0e#N*s"\x1c2\x0bD2=z"v~-z"X/\x11\x07\x12\x03\x08Dx' \
+ b'\xa0\x0bv_o\x06^\x0fv\x13<F0\x19<u\t] \x18vx++\x15\x19EJ\x19,\x009I9&MSv#l\x05o\x04\t%U' \
+ b'\xa0\x00w\n\t\x1a\x11BtyEbs\x17+i\x08O0(C$ge \x186v\x18\x07\x1fwnlw\x037hV\x1czXj\x06Yt' \
+ b"\xa0\x06.Ny_\x15W?\x19i\x13'\x1a\x0eC*\x0fh\ny!y+\x02\x1aZO\x1a\x04H\x1a\n\x16%>\x03Hwax\x0fn\x07.~" \
+ b"\xa0\tK \x14)RW#,\x13+\x1c3\x04\x083\x04%#l8sA52'P3;\x0f#@\x10-\x19\x1aXD\x11\x07\x0f{\x08D\x04" \
+ b'\xa0\x0b&Wo\x0f^\x0f1\x1a<\x118\x1aa(\x19\x1cT\x19t#+-E\x19Wp\x19:<9\x15l&\x12\x19v\x04q\x05g\x10\t"b' \
+ b"\xa0\x04w\n\x1d\x1a\x19B\x11\x04E\x1eZ\x17<{\x08\\=(\x1c:g'2\x183\x06\x08l2w~|w\x1a\x11h)%zY\x04\x06Yv" \
+ b'\xa0\x04.NiO\x1dW{Ti\x021\x1a\x06G*\x04g\nI)y\x11\x11\x1aOW\x1aEJ\x1a\x19\x1b%EvHrdx\x11\x01\x07.\x7f' \
+ b'\xa0\t\x12 4)\x12W)}#\x00O2s{3n\t#o$stB2/X2!y#\\\x00-\n6Xk\x0b\x07\x16w\x08Ew' \
+ b'\xa0\x0b&Si\r^\x0fZ]<u|\x1avD\x199])L7+ph*\x01(\x19MM:E\x01&+0v@e\x05o\x03\t"V' \
+ b'\xa0\x04t*\r\x1b\x19Bd8V\x14\n\x17\x1dl\x08T\'(A+g\tU\x18!\x04\x18\x08"w\row\t*hr\'z\\\x01\x06Yp' \
+ b'\xa0\x06.KyO\x15W\x10Yi.I\x1a,p*<\r\x1a\x10MydS\x1aP~\x1aLr\x1ai=%iWHg\\x\x0en\x07,x' \
+ b"\xa0\t\x1b \x14)\x1aW'[\x13x'2\x06z3|\x18#l3s}L2\nP3V\x06#l\x05-\x0f'XY\n\x07\x13\x02\x08F|" \
+ b'\xa0\x0b.Sy_^\x0f\x19\x1e<h^\x1a\x192\x19=Z)7E+vM*\x06\x0b\x19\x01R96|%\x1b|ftf\x05p\n\t!O' \
+ b'\xa0\x00wG;R\x11BO}E}W\x17\x0f8\x07#w\x18}~f9\x7f\x08\x7fM\x07ZtwI>wXKh\x07*zaz\x06Yp' \
+ b'\xa0\x04.oiN\x15WS=i$V\x1a${*1\x0f\neVii>\x1ahu\x1axw\x1af=%hOHc_x\x1cj\x07.p' \
+ b'\xa0\t\x13 4c\x12W\x17R\x13jQ2kr3`\x02#p\x17sCX22<2/s"\x11l-zMX\x06\x08\x07\x15s\x08E}' \
+ b'\xa0\t,\x12i\r^\x0fz#=>\x0e\x1a$k\x1aA\t)f`,i\n*Q)\x19}w:!\x18&:\x19v\x0fb\x05m\x07\t Q' \
+ b'\xa0\x00~#9R\x11B>mU\x0f]\x07w9\x07\x12r(\x15{g\r/\x18\nI\x07Bqw49wKQh\r1zZ|\x06Xo' \
+ b'\xa0\x04.KkO\x15W\\1i8:\x1aCi*(\x04\x1a)?yKD\x1a(\\\x1az^\x1a\x1a4%Q]HW^x\x15k\x07/r' \
+ b'\xa0\t\x1a\x1d4cRW<k#\r22\x01n2Xy"\t\x7f\x03\x01K2<)2\x19a"<k-rLX\x01\r\x07\x14{\x08E\x01' \
+ b'\xa0\x0b<\x12I]V\x0f\x00R=D\x10\x1a4\x07\x1aY\x1b)sb,_\x14*=1\x1al\x02:H=%Tkfoi\x05k\x7f\t R' \
+ b'\xa0\x04~\x03;R\x19Bj+U\x12X\x07o+\x07\x0bb(\rcg\x7f\x03\x08v8\x07E^w5/wCah\x1f6zS\x03\x06Wy' \
+ b'\xa0\x04>K_O\x15WQJi\x16O\x1a@\x0f*_,\x1a\x0ekyZ;\x1a\x11\x10\x1b\x1e\x08\x1a$T%x*H\x1a[x\x16b\x07/s' \
+ b'\xa0\t\x03\x104c\x12W/m\x13\x7f63s\x013k\x12#P\x10\x03\x17C2/G2*h"<e-xLX\n\x10\x07\x18v\x08Dz' \
+ b'\xa0\t.\x12iMV\x0f\x7f\x10<6v\x1aHo\x1aU\x19)I`,}\x00*D7\x19on:9\x14&D\x04fri\x05lt\t E' \
+ b'\xa0\x00>G;RYB}aU\x1f3\x07q\x13\x07\x7fU\x18Xbfy\x7f\x08\\3\x07&Uw\x19\x1ew2`h15zW\x02\x06X\x05' \
+ b'\xa0\x06?O}OUW\x1fei\x0e^\x1aW\x02*G\x1d\nf_ySS\x1aY\x00\x1a\x00{\x1a/F%pIHeZx\x17h\x070Z' \
+ b'\xa0\r\x12\x194c\x12Ww\x12#xV2~Z2Xp"c|\x03\x07H2\x12+2.Q"]X-fgX0\x00\x07\x19s\tDC' \
+ b"\xa0\x0b,\x12y]^\x0f\x05'=z\x17\x1a6j\x1aY\x0e){d,U\x01*D7\x19$h:P*%G}fug\x05j\x01\n\x1f|" \
+ b'\xa0\x02~C;RYB\x00]U(?\x07j\x1a\x07\x07\\(\x02`fRt\x08p2\x07iRw<#w9th&:z[\x07\x07Xz' \
+ b'\xa0\x04?k]GUW3\x04Y\x7fV\x1aN%*d4\x1a\x1dtiw4\x1a_\x14\x1bj\x14\x1a)`5\x0b"H\x10[x\x19P\x08/\'' \
+ b'\xa0\t\x12)6cZW*=#\x02#2RL2Kf"Hts+\x0e"]\x1b29R"%V-cZX\x1b\x0e\x07\x16\x02\x08G\x1c' \
+ b'\xa0\t,\x1eY]^\x0f#\x0b=:%\x1aPx\x1a}!)\x08\x08,A\x07* L\x1a/\x14:GL%n9f*d\x05h\x05\x08\x1f3' \
+ b'\xa0\x00.G?@\x19Bp]U)5\x07B`\x07N\x1d\x18]*fbJ\x07&y\x07\x10\x1ew\x00nx\x01\x0chF4zS\x13\x05W\x1a' \
+ b'\xa0\x04>K]G\x15WNNiD\x7f\x1a>&*V7\x1a\x03ey.U\x1aX\x01\x1b\x11\x11\x1a!Z5\x00)H,Qx\x18Y\x06/\x0c' \
+ b'\xa0\r\x12)6c\x12Wj\x08#$F2?Q2Jd"6esTB"_\x132\rM"\x1dJ-TlX@\x03\x07\x1eg\x07E\x7f' \
+ b'\xa0\t$\x12I]\x1e\x0fS,=\x1d\x08\x1ayn\x1aj\x1f)Oh,\x1e\n*\x077\x1av\n:+/%N]fZf\x05l\x10\t \x1b' \
+ b"\xa0\x02/G?A\x19B\x17~E{\x19\x07\x17b\x07U#\x18%'f;H\x07\x1ay\x07\x07\x1fgtnx\x06\x14hs7zR\x01\x06X\x0b" \
+ b'\xa0\x00~K_G\x15Xu\x1ei5~\x1aP-*\x08I\x1a\x1e{yhp\x1a\x10\x11\x1bB\x16\x1aXi5\x0e/HQOx\x1fM\x0711' \
+ b'\xa0\x0f\x12\x194bRW\x1bH#8i2pc2mh"of\x030p20 2OG2\rS-g{X^\x06\x07\x15d\x08F\x02' \
+ b'\xa0\tt\x1e[VV\x0f_"=\x0fL*\x17F\x1aTU)C8,~^*)w\x1as>J>\x065\x1c.f.f\x05hv\t\x1cN' \
+ b'\xa0\x02/G?HYB\x148Ek\x14\x076\\\x07Y\x0c\x18U+fXU\x07N\x7f\x07$ w\x04ph{\x06hDCzP\x12\x06X\x04' \
+ b"\xa0\x04>K_G\x15WT2i\x1bk\x1aP6*gD\x1a\x13}y'N\x1a\t\x15\x1b,\x14\x1a\x18\\5\x08*H*Sx\x15W\x072x" \
+ b'\xa0\r\x12)6sRWn7#0H2F_2*`"M[st8"x\'2\x17;"5;-U\x02X]\x03\x07\x19`\x08C"' \
+ b'\xa0\t<\x1e]UV\x0fG9=\x1b8\x1aR2\x1a{F)2),@1*I\x02\x1aN-:p_5\x02EfBc\x05j|\t\x1dl' \
+ b"\xa0\x02'F\x0f\tYB<fEr\x16\x07}D\x07\x17p\x18%\x0bf!-\x07\x01h\x07k}gQPh[\x1dx\x04DzM\x17\x06X\x12" \
+ b'\xa0\x04=C_UUW?jZL\t\x1aXB*pW\x1b\x0c\x1cy\x0ey\x1a\x0f;\x1b\x194\x1b\x11\r4)jH\x11Lx\x19F\x070\x05' \
+ b'\xa0\r\x13)&cRWx)\x13KL22[2 [",Ys9<"u(2zG"\x0eK-ccXY\x01\x07\x1b_\x08G7' \
+ b'\xa0\t}\x1e]GV\x0f\x10/-(e\x1a]o\x1a\x08z)\x15T,\x1dX*a)\x1ado:e"5Kvf\x1c`\x05dz\t\x1dn' \
+ b'\xa0\x02/G?IYB\x057EB\x11\x07\nK\x07\x17\x01\x18!\x1bf3:\x07\x14s\x07\x0e\x14gS_hh\x0eh\x7f>zI\x05\x06V\x1e' \
+ b'\xa0\x04>OYOUW\\\x05i\x17K\x1aK\x1a*b(\n|by>0\x1am~\x1b1\x04\x1a%H%k+H4Vx\x18L\x073\t' \
+ b'\xa0\x0b\x12\x190sSW\x1bu#]$2N-261";+\x03\n\x082\x05n2\x1b\x14"W\x12-\'\x15Wtx\x07 U\x08DD' \
+ b'\xa0\rt\x1e[TV\x0e!o=,**\x14A\x1aPW)D1,L\x1e* u\x1an=J*p5\x11\x17f\x0e\\\x05fs\t\x1dz' \
+ b"\xa0\x02$N\x0f\tYBA1U\x13i\x07r\x19\x07\nB\x17\x15_f'\x03\x07\n7\x07ySgD&h0>x\x1bCzH\x14\x06X*" \
+ b"\xa0\x04|COUUWXSj/\x15\x1at`*\x1e^\x1bK\x1by?e\x1a?,+ <\x1b'\x014,vH\x01Ex\x14L\x071\x14" \
+ b'\xa0\x0f\x12)4sSW/)#JL2GO24R"PEsz%2\n\x072O4"2--N\x07Wk{\x07\x1eR\x08GR' \
+ b'\xa0\tt\x1fOV\x1e\x0f.\x15=\x15j*\x0b\x7f\x1aK\x11)D[,QC*\x18+*+wJ\x10 5L\x04f\t^\x05f\x04\n\x1c\x01' \
+ b"\xa0\x02-\x0e\x1fIYB(&Ehm\x07\x0f5\x07\x1bg\x17\x10|f(u\x07\rW\x07\x1fqgG@hR#hyMzK'\x06X5" \
+ b'\xa0\x04wCOGUW*AYko*\x13S*\x1fg\x1b5\x12y)S\x1a+%+\x04/\x1a {5\x1e\nH\x15Kx\x1eO\x074\x19' \
+ b'\xa0\x0f\x12\x1923SW\x1b>#)D2:M2\x1dE"8/\x03\x16\x1d"z{2,\x15"+"-D\x1cg\n{\x07#]\x08Fb' \
+ b'\xa0\n|\x1fO\x07\x1e\x7fz\x17=\x05a\x1afx\x1a,\x06)@],kJ*\x11\x1a*\x05b:y25Mwf}]\x05g\x06\n\x1b\x10' \
+ b'\xa0\x02T\n\t\x03YBc-U\x1eT\x06\x15mw1\x0e\x17Q fo8\x07:y\x07@\x10f\x0bsh\x7fuxcSzG\x16\x06WC' \
+ b'\xa0\x04~C]GUW3Pi>|\x1a9D*\x08T\x1b\t\x01yS"\x1aX\x0f\x1b@\x18\x1a/z5\x1a\x1cH)Jx\x1cJ\x074,' \
+ b'\xa0\x0f\x13\x1963SW\x16V#\nf2Eb2<Y"U.\x03!\x08"\\\x0f2\x1b&"y/-O/g\x12u\x07&Y\x08Es' \
+ b'\xa0\t$;O\x16\x16\x0f\x0f$=Kg+R\x15*\x13#9\x10Y,\tC*;<*LxJp85P\x0ffj_\x05a~\n\x19 ' \
+ b"\xa0\x02D\n\r\tYBW7UGM\x07d\x15w}A\x17zUfVh\x07o6\x07eLgP%h'Kx&RzA\x1b\x06XS" \
+ b"\xa0\x04~G]EUWLxi\x14o\x1aeA*\th\x0bw\x17y=`\x1aw'\x1b+*\x1b'\n5\x1f\x14H\x1fFx\x16=\x076:" \
+ b'\xa0\x0b\x12\x19&3\x13X5\x04#jT2-12\t9"*-\x03$."]\x042z\x10"=%-6\x1dg\x1dl\x07\x1eM\tE\x0e' \
+ b'\xa0\ne\x17\x7f\x06\x16\x7f>1-b~+\x06\t\x1a-\x1e*^\x10,Qh*\x18S\x1aw\x04J\x17[5bJf\\^\x05bz\n\x1a8' \
+ b'\xa0\x02D\n\r\tYBI>U\x1ac\x078\x01w:$\x17n<fXc\x07X\x17\x07H.g\x1f\x11h\x14WxJXzD \x06Yr' \
+ b"\xa0\x06tBO\x15UGz7j'\n*\x00t+F\x17\x1b@:yam\x1aZ<+ T\x1bG24HhHoJx\x14;\x075P" \
+ b'\xa0\x0f\x12\x1903\x13W\x1d/#b/2/22\x1f2":\x1c\x03+\x0e2\x1ce22\x02"P\x14-$0g\x15r\x07\x1f]\tG(' \
+ b'\xa0\x0ee\x13o\x06\x1e~B\x7f-bg+K\x1f\x1aw!:\x02\x0f,_g*oA*`\x01JDV5\\Tf0]\x05^\x03\n\x1aM' \
+ b'\xa0\x02T\x0b\t\x03\x19BS*U\x075\x06,bv%|\x17O\x15f7;\x07Hh\x07K\x04f\x0eghkvxZUzD\x1c\x07X\x03' \
+ b'\xa0\x04uC_UUW\x1dzZ\\\r**c*5y\x1b_*yFd\x1ab"\x1b{;\x1b7\x1746xH\nAx 5\x074\\' \
+ b'\xa0\x0b\x12\x19\x101\x13XU\t#?@2U 2\x12\x1f"\\\x08\x03H\x142\x1eX2(w"S|-\x1bLg>l\x07$E\tG4' \
+ b'\xa0\ne;o\x06\x1e\x7fd\x1d-Zf+a\x06\x1a|\x149\x1av,9k*e8*)\x03JGH5Wofg^\x05d\x03\n\x1aT' \
+ b'\xa0\x02U*\r\x0b\x19B}\x1dEt<\x06SkwV\x0f\x17r*f.U\x07c\x04\x07E#f$zh\x02pxD]z@+\x07W\x05' \
+ b'\xa0\x04uc_EUW\x1aKZo\x01*\x0bb*)y\x1b )y"v\x1a0+\x1b@A\x1b/\x135,\x03H\x02Cx\x18D\x075^' \
+ b'\xa0\x0f\x12\x19"3\x13W4m#:a2\x1f,2\x05&""\x11\x03GI"ek2W\x06"L\x07--3g=m\x07 I\tI<' \
+ b'\xa0\ngWo\x0c\x1e\x7f\x02\x02-.\x16+8/\x1a\x7fK*|=,g(*Nt*\x10@KH\x035\x18\x14f\x17Z\x05^\x02\n\x19P' \
+ b'\xa0\x00\x19\x0b1RYR\x02"Ek.vrSv}o\x17\x0c\x00f79\x07\x11_v~~f|Xhh\tx\x06[zC/\x06Y~' \
+ b'\xa0\x00}\x06_UUX\n\x06ZK\x14\x1att+(\x15\x0bt>z3\x0f\x1a\x110\x1b6I\x1b4.4EoH\t:x!3\x077P' \
+ b'\xa0\x0bR)\x023\x13X;"#\tV2\x10*"v\x1f"\x05\x13svC"7_2R{"7\x0c-!7gFo\x07&>\tI%' \
+ b'\xa0\x08fSo\x0c\x1e\x7fw\x17-y\x10+8C\x1abC:\x04I,\x1b#*\x1cr*"2K<\n5\r\'f\x15\\\x05a\x0b\n\x1a/' \
+ b'\xa0\x02T\x0b\t\x03YB`\x17U\x00*\x06\rav\x05|\x17J\x15f 2\x07\x19k\x07)\x08f\x07jhq}xs]zA>\x06Z^' \
+ b'\xa0\x04vBO\x15UW\x13Oi/v*(k+0\x16\x1bs9ygV\x1aN4+\x1dL\x1bX*4<kHr;x\x1f,\x078\x19' \
+ b'\xa0\x0fS\x11\x101\x13Wjv#\x0eI2( "v!"]t\x03C\x042\x08g2$t"{~-+HgRa\x07*B\tI\x02' \
+ b"\xa0\x0e'Ri\r\x16~\x12m-0\x1c+Pb+\x1d\x05:IZ,X\x0f;\x03'*I[K\x00*5L\x14f\x1bT\x05\\z\n\x17\x01" \
+ b'\xa0\x04Y\x0b)RYQ\rzEU\x1dvzSv\x01q\x17$\x03f\x17\x1e\x07"e\x06\x11{f|Uhj\rx\x07_z?/\x06Y2' \
+ b'\xa0\x06eBO\x15\x15Gv#Z_\r+\x0c\x03+J\x1f\x1baOy\x1e~\x1a>B+\x0b`\x1b 74VWHX=x\x1f$\x077q' \
+ b'\xa0\x0fR\x11\x021SW]`#m=2\x0c\x0f"k\x02"2n\x03\x053"RK2}e"+f-\x12Ygbo\x07#D\x08KR' \
+ b"\xa0\x0c'Sk\x0c^~wg-5\x14+b_*\x1fc:8U,6@+l\x11*SZK]\x1955\x1ef\x11T\x05^\x03\t\x19Z" \
+ b'\xa0\x02\x19#1RYBo\x0cEg\x0cv{Pv|f\x17\x1fwVz(\x07\x11UvwqfsKha\x19x\x0f^z:&\x06Z\x0b' \
+ b'\xa0\x06e\x02_U\x15Gs~Zq\x1f+\x12\x01+N%\x1b\x1cKz\x15\x07\x1a\x1c:\x1b\x13V\x1b274SeH\x02<x\x1c0\x079S' \
+ b'\xa0\x0b\x12\x11\x021SXi6#zl2\x1d\x1d2\t\x1f")s\x03\rK"`P2=c"jh-&ogxe\x07$1\x08J;' \
+ b"\xa0\n'ri\rV\x7f$\x04-u/+\x7fi+X\x11:Sd,\rR;\x19\x12*^TK\x18 5J@f0Z\x05]w\t\x17G" \
+ b'\xa0\x00T\x03)S\x19R;\x0cUR\x15\x06\x00Mv\x0bo\x173vfo1\x07#T\x06\x11nf\x10Fhd&x\x1fiz:.\x06\\\x7f' \
+ b'\xa0\x04d"MU\x15WHdjH\x1a+I\n+d,\x1boLz\x1a\x06*\x02H+*c\x1bc?4SfH\x06@x 2\x078A' \
+ b'\xa0\t\x13\x11\x100Sh1$#,i2L+2\x12\x1c"Vn\x03g(2\x1dl2/r2\x03~-2Xg~i\x07&9\x08J<' \
+ b'\xa0\x0e?Ry\rV~\x1e\\-06+\x1e\x07+e\x1d:_k,C5;0H*\x1dzK\x1fJ5g\x07f\x07T\x05\\l\t\x17A' \
+ b'\xa0\x04\x18\x072R\x19Q%|U\x04\x13vS6v[Q\x07nKf\x10\x08wvAv\x7fUfo1hMBx"fz<8\x06Z~' \
+ b'\xa0\x06e\x02_\x15\x15G[jZ_)+1\x03+iB\x1b(Yz(\r\x1a;V\x1bis\x1bTK4bYH^8x\x1f(\x079A' \
+ b'\xa0\x0bR\x11\x021SX`\x19#oc2\x17\x04"d\n"\x1bf\x03\x135"\\Z2Rc"]k-\x18Zgjc\x07,0\x08L;' \
+ b"\xa0\x0c'ZI]^~74-\x18*+~s+:':X\x12,=`;#V+c\x0cK\x08W4dqVPP\x05^\r\t\x18=" \
+ b'\xa0\x04\x1bC2RYQ6LDzbv9 v7?\x17\t:e%ewn"vj>fW\x1ch9^x2bz:6\x06[\x00' \
+ b'\xa0\x06eBO\x15\x15Gv1ZB\x17+%\x1c+aO\x1bPiyB~\x1abN+\rp\x1b>Y4pVHc;x\x1e)\x07:<' \
+ b'\xa0\tR\x11\x103Sh\r\r#[r22+"\x7f\'"Gf\x03?"2\x16_2\x08h"~\x04-0Tg{a\x07(8\x08JC' \
+ b'\xa0\x0c>^Y]V~b<-dF+,#+nN:\x03\x1e,bT;bg+!\x13K4z4\x7fSVUP\x05W~\t\x16C' \
+ b'\xa0\x04\x1bG1RYQRYDXqvW,vRP\x07uKe@n\x07\x054v{Jfk+hBHx?gz7*\x06[\x03' \
+ b'\xa0\x04eBO\x15\x15WJ2Z\\\x17+:\x08+i<\x1b\x1bXyR}\x1a_J+"k\x1bME4Z\\Hd9x".\x07;;' \
+ b'\xa0\tS\x11\x12pShG\x0c#\x03h2"\x10"r\x07"\x0f@\x03; "iH2\x1cI2\x0eP-\ryg\x17g\x07&3\x08KF' \
+ b'\xa0\x0c?ZY]V~^1-\x103+\x17\x18+mD:g\x0f,CV;0e+1\x11KB]4shVNW\x05Z\x7f\t\x17A' \
+ b'\xa0\x04\x1aG2RYQUYT&Wv(\x1bv>@\x07f3e\x0fLwV\x1eve4f_\x15h2}xKoz85\x06\\\x01' \
+ b'\xa0\x06vB_\x15\x15Gx=i\x11p*\x15y+hE\x1bVcyV`\x1aHN\x1blp\x1b1T4VgHo6x\x1f5\x07<;' \
+ b'\xa0\tS\x11\x02aSh5-#PL2!\x00"n\x00"DB\x03J\x05"hE2tJ"]T.\x0f\x08g&a\x07(9\x08LJ' \
+ b'\xa0\x0c?^YU^~X7-M)+O-+}T:\x19),g?;En+A*K.oD\riV?Q\x05[\x01\t\x16C' \
+ b'\xa0\x04\x1aG2RYQYXT\x18AvN*vHK\x07wCe2EwX,vuHfj\x1ehEexYsz60\x06[\x07' \
+ b'\xa0\x06gJo\x15\x15GaRYm{+?\x1e+xe\x1b{\x06yvu\x1aIl+9\x0b\x1b^f4lKHE8x$&\x07<<' \
+ b'\xa0\tR\x10\x02qSh#B#pe2\n\t"Kz";K\x03M#"HH2n;"qS-\x08ng\r[\x072*\x08MP' \
+ b'\xa0\x0c>ZIW^~\x15c-TE+ 8+bf:zR,Zy;5}+q.K+\tD\x12<V\x1aQ\x05_\x08\t\x16C' \
+ b'\xa0\x04\x1aG2RYQU{T\x0cJv\x1e\x15v)1\x071/e\x18AwC\x0cv&&fL\x0bh-vxavz8N\x06[\t' \
+ b'\xa0\x04$BO\x15\x15W\x0crj\x03\x05+U\x10;\rJ\x1b oyN}\x1alT+\x00y\x1bTS4d[Hg9x!1\x07<;' \
+ b'\xa0\tS\x10\x10`ShOG#Hm2H\r"|~"(@\x03v52\nJ2\x13@2\x15D.\x13\x0bg-]\x07/*\x08KJ' \
+ b'\xa0\x0c\x7f\x1e]U^~*--\x07;+b:+\x18n:\x13?-s\x02;\\\x05+?-KPoD\x1deVDR\x05W\n\t\x15A' \
+ b'\xa0\x04\nG6 YQt\\T\x135v\x03sv\x01\x15\x070\x07e8<v\x17ov/\x0cfK`i\x11%\x08\x08pz0>\x06\\\x06' \
+ b'\xa0\x06g\x02\x7f\x15\x15GE*YBv+0\x15+\x7fX\x1b:|z7\x0f\x1a\x16[\x1bj\x05\x1bLZ4iQHJ/x\x1e#\x07>5' \
+ b'\xa0\tB\x10\x02pSh\x1b5#ju2\x17~"Tr"(A\x03\x14E"@<2a92\x03W-\x10qg\x1fW\x070*\x08NT' \
+ b'\xa0\x0c~\x1e]W^~\x0e\x0b-ER+Y@+\x12x:3_-N";R\x16+UEKa\x1fD3,V\x1bQ\x05V\x02\t\x149' \
+ b'\xa0\x04\x1bG2BYQeADS7v\x0e\x06v\x1e-\x077!e/]w:\x05vK!fU\x00i%\x03xtzz74\x06\\\x01' \
+ b'\xa0\x06\'"O\x15\x15G~oYD{+\x1b\x0b;\x02L\x1b!gz\x1f\x13\x1aPS+\x01u\x1bEN4YmHy5x&\x18\x07>(' \
+ b'\xa0\rC\x10\x02`ShP\t#\x1aj2\x18q"\\e"*1\x03y>"p82\x7f62\x12;.\x01\x0bg9R\x07-+\x08NP' \
+ b'\xa0\x0c~\x1e]\x17^~\x00J-\\8+V<+\x0ft:&h-O\x16;Z\x1b+L_KT\x17D-!VrM\x05Q\x0f\t\x158' \
+ b"\xa0\x02CG6@YR \x02D\x7f\x16vl^ft\n\x07\r\x07e\x06\x14v\x03cv$\x0ef'bi\x10*xy{z,:\x06^\x02" \
+ b'\xa0\x02gJo\x15\x15Hz\x0fYT|+\x08\x16+vl\x1b,\x08y/p\x1a*V+\x0f\x13\x1b\x1fs4sQHP3x&\r\x07@(' \
+ b'\xa0\rY\x10\x12pShf\r$]\x00"w\x04"Sl"3+\x03p\x13"Z,2\x0022\x0bS-\x0b|g:R\x072*\x08NP' \
+ b'\xa0\x0c~\x1f_\x17V~\x04.-g]+W`+6\x04:Uh-n\x03;\n\x1c+peKm7D>\x0fVwL\x05Z\x7f\t\x156' \
+ b'\xa0\x06\nG6 XQ6fT63v\x02jv\x14\x05\x074\x05e9-v/mv:\x0efGfi\x0c\x1d\t\x16\x00z/A\x06^\x03' \




    stream=io.BytesIO(b);
    print("START DECODING")
    while 1:
            #remove marker
            stream.read(1)

            #decode 45byte data
            try:
                decode_data(stream.read(45))
            except:
                print("END DECODING")
                file_object.close()
                break;

a1eks
Posts: 18
Joined: Sun Jan 21, 2018 6:26 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by a1eks »

So, upgrade.

When I connect to the EMG port I get a nice signal, but I don't get anything else meaningful-similar to the official EEGsw on other channels. :cry: There must be something about bit order that I am not doing well.

What I am very confident of is that 45bytes carry samples of all 26 channels. I measure 10 seconds and I received 460bytes (45 bytes +1byte marker)x10sec, which tells me that the data must be in 45bytes, and in particular 44B+4bits as the first four bits of the first byte are not used (are always zeros)


How do I know that there are 26channels? If I load EDF generated by their software in Matlab/EEGLAB I can see 26 channels, on the other hand the math gives exactly the same results, so: 44.5Bytes x 8 - 44unused zeros at the beginning = 312bits in total. 312bits/12bit per channel=26channels in total!

Now, how they are distributed, I still didn't figure out. For me it is a mystery how I get the ECG only on the last channel, but not on the others. If somebody has some idea how I might decode the channel info, let me know.

Thomas
Posts: 74
Joined: Wed Mar 04, 2020 3:38 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by Thomas »

Hi A1eks,

Thanks for the upgrades.

I see the comment about the byte swapping, saying the first byte is the least significant one and the second the most significant one.
I am a bit confused because just bellow, this is not what the code does and another comment (which comments what the code actually does) is contradictory with the byte swapping: First byte represents the 12 most significant bits and the second one represents the 4 least significant bits.

Also, I think the data for each channel comes together, and so the byte swapping (if needed) should be the last step after extracting the bits of data.

The script you shared starts by swapping all the bytes of the buffer. I don't think this is correct.

Note how it is done in the KT88 1600 driver: Looping through each channel, it first reads 2 bytes for the channel, and then arrange them to represent the data. Swapping the whole frame from the start is very likely shuffling the data.

When you spy on the data using the provided EEG software, can you change the amount of channels that you want from the amplifier ?
This would be an interesting way to see if the amount of data still follows the same pattern.

Have you tried contacting Contec to learn more about the formatting of their data ?

Hope this helps a bit, let us know how you get on.

Cheers,
Thomas

a1eks
Posts: 18
Joined: Sun Jan 21, 2018 6:26 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by a1eks »

Hi Thomas,

You are welcome, thank you for your suggestions!
Thomas wrote:
Fri Oct 29, 2021 10:09 am
Also, I think the data for each channel comes together, and so the byte swapping (if needed) should be the last step after extracting the bits of data.

The script you shared starts by swapping all the bytes of the buffer. I don't think this is correct.
So, you suggest loading the data as it is, and then swapping bits like thins:
ch1 = [0000 1111][1111 1111] ---> [0000 1111][1111 1111] :?:
This is, at leas, what was done in the code, if I understood correctly:

Code: Select all

l_ui16Value = (((uint16)l_pBytesRead[0]) << 4) | (l_pBytesRead[1] & 0xF);
Thomas wrote:
Fri Oct 29, 2021 10:09 am
Note how it is done in the KT88 1600 driver: Looping through each channel, it first reads 2 bytes for the channel, and then arrange them to represent the data. Swapping the whole frame from the start is very likely shuffling the data.
That natural way to do it, but the difference is that each channel is always represented by two bytes, and in the case of KT88 that varies, so I would have to read sometimes 3 bytes, and then shift them differently for each channel. For that reason, I did read all 45bytes at once and tried to manipulate them.
Thomas wrote:
Fri Oct 29, 2021 10:09 am
When you spy on the data using the provided EEG software, can you change the amount of channels that you want from the amplifier ?
This would be an interesting way to see if the amount of data still follows the same pattern.
No, unfortunately, you can switch off the channel in the software, but that is just not visualizing it, the amplifier still sends all the data.
Thomas wrote:
Fri Oct 29, 2021 10:09 am
Have you tried contacting Contec to learn more about the formatting of their data ?
Yes, I have contacted them and waiting for their response, but I don't expect much. Maybe I am wrong.
Thomas wrote:
Fri Oct 29, 2021 10:09 am
Hope this helps a bit, let us know how you get on.
Thanks. For now, I tried to disassemble the exe and to find what exactly is doing.

Here is part of the assembly code responsible for the bit decoding
https://github.com/miladinovic/KT88-160 ... n/kt88.asm

I cleaned it and try to make some comments.

So, what I figure out is that the code reads 45 bytes (there is a loop 45 time that evokes ReadFile function to read com port and reads byte per byte)

routine
seventh_function_read_serial_45B:

then it compares the read byte to the markers A0h or B0h just to ensure that it is not contained in the next 45byte sequence.

Then for each byte read it calls routine

fift_funnction_process_byte from which decode_bytes is then called to perform further processing.

second_proc_step routine loops to accumulate several samples then first continue and show them

I have a feeling that third_proc_step or process_load_data do the thing of bit decoding, maybe I am wrong.

Best,
Aleks

Thomas
Posts: 74
Joined: Wed Mar 04, 2020 3:38 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by Thomas »

Hi Aleks,
So, you suggest loading the data as it is, and then swapping bits like thins:
ch1 = [0000 1111][1111 1111] ---> [0000 1111][1111 1111] :?:
This is, at leas, what was done in the code, if I understood correctly:

Code: Select all

l_ui16Value = (((uint16)l_pBytesRead[0]) << 4) | (l_pBytesRead[1] & 0xF);
Yes, that's what I think, maybe not on two bytes as the format seems different, but the idea would be similar.
Also I think the result to this code shifting is slightly different:
ch1 = [0000 1111][1111 1111] ---> [0000 0000][1111 1111]
1. ((uint16)l_pBytesRead[0]) = 0000 0000 0000 1111
2. ((uint16)l_pBytesRead[0]) << 4 = 0000 0000 1111 0000
3. (l_pBytesRead[1] & 0xF) = 0000 1111
4. (((uint16)l_pBytesRead[0]) << 4) | (l_pBytesRead[1] & 0xF) = 0000 0000 1111 1111

That natural way to do it, but the difference is that each channel is always represented by two bytes, and in the case of KT88 that varies, so I would have to read sometimes 3 bytes, and then shift them differently for each channel. For that reason, I did read all 45bytes at once and tried to manipulate them.
I agree that with the format you suggests, it would be a pain to do. But still, I believe that swapping bytes on the whole stream without knowing for sure that it is needed will not help.
Here is part of the assembly code responsible for the bit decoding
I had a look at the assembly, thanks for the great effort!
I got lost in the decode_byte function. This is getting a bit too much time consuming for me, sorry.
If you can share the exe, I could try to run it through AIDA64 to see the graphs (no promise!! ;) )

Another idea if you cannot change the amount of channels being output: Could you saturate only every odder channels ? This could potentially help seeing a separation between samples ? Ideally, zeroing the other channel to see a clear cut. I don't know if this is doable for you ?

Sorry for not being more helpful. I hope it does help you push a bit further though.
Let us know how you get on!

Cheers,
Thomas

a1eks
Posts: 18
Joined: Sun Jan 21, 2018 6:26 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by a1eks »

Hi Thomas,
Thomas wrote:
Fri Oct 29, 2021 5:20 pm
ch1 = [0000 1111][1111 1111] ---> [0000 0000][1111 1111]
1. ((uint16)l_pBytesRead[0]) = 0000 0000 0000 1111
2. ((uint16)l_pBytesRead[0]) << 4 = 0000 0000 1111 0000
3. (l_pBytesRead[1] & 0xF) = 0000 1111
4. (((uint16)l_pBytesRead[0]) << 4) | (l_pBytesRead[1] & 0xF) = 0000 0000 1111 1111
Thank you, will try this, and let you know.
Thomas wrote:
Fri Oct 29, 2021 5:20 pm
I got lost in the decode_byte function.
I can feel that it took me some time to find the relevant part and to mark them, but I don't have much experience with assembly language (I can say this is the first time I was working with it).

here are the exe files (folder and installation file)
https://filesender.garr.it/?s=download& ... 9928dc33ab
Thomas wrote:
Fri Oct 29, 2021 5:20 pm
Another idea if you cannot change the amount of channels being output: Could you saturate only every odder channels ? This could potentially help seeing a separation between samples ? Ideally, zeroing the other channel to see a clear cut. I don't know if this is doable for you ?
In theory, it should be doable. 1mV DC with reversed polarity on every other channel should do the thing, of course, if filters do not cut the dc. I might try to saturate only the first channel (Fp1), because I am not even sure if it really corresponds to the first 12bits of the 45byte chunk :|

a1eks
Posts: 18
Joined: Sun Jan 21, 2018 6:26 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by a1eks »

Hi Thomas,

Here is the output of the channels when each single is connected to the ground and others are saturated:

https://github.com/miladinovic/KT88-160 ... 882400.txt

Edit:
Here are some new findings:

Code: Select all

Fp1 0b10100000 00001100 01111111 01111111 01111111 01111111 01010010 00001111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111101010101000100000110010100000000000001110010000101111011
Fp2 0b10100000 00001011 01111111 01111111 01111111 01111111 01010010 01110000 01111111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111101000010000100000001110000000000000001110000000101111010
F_3 0b10100000 00000111 01111110 01111111 01111111 01111111 01100111 01111111 01111111 01111111 00001111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100101010000100100101111000001101011101110000000101111100
F_4 0b10100000 00001111 01111101 01111111 01111111 01111111 01000111 01111111 01111111 01111111 01110000 01111111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100110111000100010100011001101111011101110111011101111011
C_3 0b10100000 00001111 01110011 01111111 01111111 01111111 01001111 01111111 01111111 01111111 01111111 01111111 01111111 00001111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100101010000100010101011001000001011101110111100001111101
C_4 0b10100000 00001111 01101111 01111111 01111111 01111111 01001111 01111111 01111111 01111111 01111111 01111111 01111111 01110000 01111111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100100111000100010100100000000110011101110111011001111100
P_3 0b10100000 00001101 00011100 00011010 01101111 00110100 01110010 01101101 00101100 01011110 01101101 01100010 01011010 01011101 01110110 01011111 00001110 00000000 00011111 01101101 00101011 01101101 01101110 00101000 00101101 01101110 01000011 00101010 01101101 00000110 00111010 01101110 01110110 00100111 01101110 00011011 00011011 01101110 0101110001101011000100000101100100000000000001110000001101110111
P_4 0b10100000 00001111 01111111 01111110 01111111 01111111 01011111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01110000 01111111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100001001000100000010011100110010000001110000001001111111
O_1 0b10100000 00001111 01111111 01111001 01111111 01111111 01101111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 00001111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100001001000100000011001101101100011101110111111001110111
O_2 0b10100000 00001111 01111111 01110111 01111111 01111111 01010110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01110000 01111111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100100010000000000101100000000000000001110000010001111010
F_7 0b10100000 00001111 01111111 01001111 01111111 01111111 01001111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 00001111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100110011000100010110100001001100011101110111011101111100
F_8 0b10100000 00001111 01111111 00111111 01111111 01111111 01001111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01110000 01111111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100100000000100010100111001000111011101110111101101111100
T_3 0b10100000 00001111 01111111 01111111 01111100 01111111 01001111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 00001111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100011110000100010100101000101100011101110111100001111011
T_4 0b10100000 00001111 01111111 01111111 01111011 01111111 01100111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01110000 01111111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100001110000100010010010100000100011101110000000101111010
T_5 0b10100000 00001111 01111111 01111111 01100111 01111111 01010111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 00001111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111100011101000100010100101001010001000001110000001001111010
T_6 0b10100000 00001111 01111111 01111111 01011111 01111111 01011011 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01110000 01111111 00000000 01111111 01111111 01111111 01111111 01111111 01111111 01111110 0111111101001110001000100010011001101100000001110010000101111011
F_z 0b10100000 00001111 01111111 01111111 00111111 01111110 01001111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 00001111 00000000 01111111 01111111 01111111 01111111 01111110 0111111100110001000100010101101101101010011101110111100001111011
P_z 0b10100000 00001111 01111111 01111111 01111111 01111101 01011111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01110000 01111111 00000000 01111111 01111111 01111111 01111110 0111111100110010000100010111011001010100000001110001001101111011
C_z 0b10100000 00001111 01111111 01111111 01111111 01110011 01000111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 00001111 00000000 01111111 01111110 0111111101000000000100100110001000000111011101110111011101111100
Pg1 0b10100000 00001011 00111111 01111111 01111111 01101111 01101111 01111111 01111111 01000110 01111110 01111111 01100001 01111111 00001100 00001111 01111111 00101100 01111111 01111111 01111111 00101000 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 11111110 01111111 01111111 01111111 01110000 01111111 00000000 01111110 0111111100100001000100010011110100001011011101110101110101111100
Pg2 0b10100000 00001111 01111111 01111111 01111111 00011111 01010111 01111111 01111111 01111111 01111111 01111111 01110011 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 00001110 0000000001011111000100100111001001010011000001110100011001111000
It seems that the data for EEG channels are encoded with 11bits starting from 8byte and further (see zeros pattern). However, there is also distinguishable patterns in the first couple of bytes (sure, we need to ignore P_3) that probably somehow adds an additional bit to the 11bits making it 12bit, as described by the system :? :?:

a1eks
Posts: 18
Joined: Sun Jan 21, 2018 6:26 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by a1eks »

New upgrade,
in theory, this should be the correct protocol. I am not sure about the bit order yet, but that could be also solved.

x-used bits for the corresponding channel
0-unused bits
1-irrelevant bits for the corresponding channel
0b10100000 - stream init marker

Code: Select all

Fp1 0b10100000 000011xx 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
Fp2 0b10100000 00001x11 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
F_3 0b10100000 0000x111 0111111x 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
F_4 0b10100000 00001111 011111x1 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
C_3 0b10100000 00001111 0111xx11 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
C_4 0b10100000 00001111 011x1111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
P_3 0b10100000 00001111 0xx11111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
P_4 0b10100000 00001111 01111111 0111111x 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
O_1 0b10100000 00001111 01111111 01111xx1 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
O_2 0b10100000 00001111 01111111 0111x111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
F_7 0b10100000 00001111 01111111 01xx1111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
F_8 0b10100000 00001111 01111111 0x111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
T_3 0b10100000 00001111 01111111 01111111 011111xx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
T_4 0b10100000 00001111 01111111 01111111 01111x11 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
T_5 0b10100000 00001111 01111111 01111111 011xx111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
T_6 0b10100000 00001111 01111111 01111111 01x11111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
F_z 0b10100000 00001111 01111111 01111111 0x111111 0111111x 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
P_z 0b10100000 00001111 01111111 01111111 01111111 011111x1 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
C_z 0b10100000 00001111 01111111 01111111 01111111 0111xx11 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
Pg1 0b10100000 00001111 01111111 01111111 01111111 011x1111 01111111 01111111 01111111 01111111 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 11111110 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111110 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111
Pg2 0b10100000 00001111 01111111 01111111 01111111 0xx11111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111 01111111
CH1 0b10100000 00001111 01111111 01111111 01111111 01111111 0111111x 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111111 01111111 01111111 01111111 01111111 01111111
CH2 0b10100000 00001111 01111111 01111111 01111111 01111111 011111xx 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111 01111111 01111111 01111111
CH3 0b10100000 00001111 01111111 01111111 01111111 01111111 01111x11 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx 01111111 01111111 01111111
CH4 0b10100000 00001111 01111111 01111111 01111111 01111111 01xx1111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0xxx1111 0xxxxxxx 01111111
CH5 0b10100000 00001111 01111111 01111111 01111111 01111111 0x111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 01111111 0111xxxx 01111111 0xxxxxxx

a1eks
Posts: 18
Joined: Sun Jan 21, 2018 6:26 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by a1eks »

Finally, I manage to get it working in OpenVIbe. The bits location are 100% correct for each channel, and I assume also the bit order, but not completely sure...
https://www.youtube.com/watch?v=HBjhafKXALY

a1eks
Posts: 18
Joined: Sun Jan 21, 2018 6:26 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by a1eks »

Upgrade.

Bit order.

I found the easiest way to figure out to generate an artificial signal of relatively low frequency (1 or 2hz) and to connect it to the input of the amplifier.

Image

I used the analogue one, as the digital new ones didn't have options to generate the signal <1Hz, which I needed to test the cut-off freq of the HP filter of the device.

So, I set it approx to 2Hz and if the bit order is somehow wrong, you would get something like this:

Image

After playing a little bit, I managed to figure it out.
Image

CH5 0b10100000 00001111 01111111 01111111 01111111 01111111 0x111111 ... 37bytes ... 0111yyyy 01111111 0zzzzzzz

CH5 u_int16=0000yyyyxzzzzzzz

Furthermore, the HW investigation of the double-stage amplification and HP filter with a cut-off at 0.034Hz a
https://patents.google.com/patent/CN103505200A/en

A further indication is the use of microcontroller MSP430F247 (also cited in the patent documentation)
Image Image
Image

EDIT.

Channels with 2 bits have this protocol.


Odd ch
CH4 0b10100000 00001111 01111111 01111111 01111111 01111111 01xw1111 ... 37bytes ... 0yyy1111 0zzzzzzz 01111111


CH4 u_int16=0000wyyyxzzzzzzz

a1eks
Posts: 18
Joined: Sun Jan 21, 2018 6:26 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by a1eks »

For now, I made an LSL python script for streaming in a local network, next stage is an implementation in C++ for OV.
https://github.com/miladinovic/Contec_K ... reaming.py

If anyone have a better idea for bit shifting in the 2nd part, let me know :lol:

a1eks
Posts: 18
Joined: Sun Jan 21, 2018 6:26 pm

Re: Contec KT88-2400 - 24 channel EEG

Post by a1eks »

I made also the script for KT88-1600 (18 channels)
https://github.com/miladinovic/Contec_E ... reaming.py

Now, I need to implement the drivers.

Post Reply