BAP reverse engineering (volkswagen PQ)

This is kind of a loose data collection on BAP (for the PQ plattform) collected from various data sources and reverse engineering.

VW uses BAP (German: “Bedien- und Anzeigeprotokoll” ~ control- and display protocol) for communication between a control unit and a display unit. BAP is meant to transport data event based instead of the usual broadcast style (e.g. ECU hot-lamp = bit X in message Y, broadcasted all 10ms). Instead data (e.g. data to be displayed or menus to be built in the telephone menu) is transmitted once and updated as necessary. BAP is the successor of DDP (Display Daten Protokoll ~ display data protocol).

Everything in Volkswagen-group terminology is German, so are some of the abbrevations.

The control unit generating the data to be displayed is called “FSG” (Funktionssteuergerät ~ functional control unit).

The control unit displaying the data is called “ASG” (Anzeigesteuergerät ~ display control unti). In PQ this is either the cluster’s display (called MFA) or the radio-navigation unit (only for certain clima-related data).

In general different control units have different CAN-IDs they emit data from. That is no different in BAP. Certain CAN-IDs are related to BAP-traffic. In vw terms, those messages will be called something like BAP_xxx (e.g. BAP_Clima, BAP_NAVI, BAP_Compass, …) depending on their function. This is the communication channel from FSG (clima control, navigation system, compass) to the ASG (display). The communication backwards (e.g. cluster confirming displaying of data or alive-status) also has its own CAN-IDs / messages (e.g. BAP_ASG_0x, BAP_Anzeige). The FSG sends a ‘request’ to the ASG which answers with a ‘indication’ and vice versa.

Obviously nearly all data is routed from the different CAN-bus of the car (CAN-powertrain for PDC/PLA & old ACC, CAN-comfort, CAN-infotainment, CAN-extended) to the gateway which finally routes to CAN-cluster.

Despite its alive-functions (called ‘heartbeat’) data is only transmitted event-based or based on a request by FSG or ASG. No “unasked” transmissions take place.

A BAP-message can consist of multiple CAN-messages. Each BAP-message has a BAP-header which specifies lsg_id, fct_id and op_code in addition to the BAP-message’s data.


There are two types of BAP-messages: short (less or equal 6 BAP-data-bytes) or long (more than 6 BAP-data-bytes). DLC is variable.

  • the size of the BAP-header is always 2 bytes
  • BAP is big-endian (example: BAP-header is of size 16bit, e.g. using data[0] and data[1] of the CAN-message. Big-endian means that the MSB of the header is in byte0|bit7

BAP-payload-data (BAP-application-data <=6 (less or requal than 6 bytes) -> 1 CAN-message (dlc maximum = 8bytes) is enough to transmit all data. To indicate that it is a short-BAP-frame the MSB of the CAN-data[0] of the CAN-message must be clear (must NOT be set).

BAP short frame + example

If there is more than 6bytes of BAP-data to be transmitted, the BAP-message must be split into multiple CAN-messages. The first CAN-message of this BAP-message is special, since it announces the amount of data to be expected (BAP-preample) as well as the BAP-header. The following CAN-messages have a simple counter (BAP-index) plus the BAP-data. DLC is always according to needs.

BAP long frame
example of a long BAP-message

derived from:, see his python-class for a excellent python implementation

In the screenhot below you can see a decoded BAP-message which shows the radio station (“ANTENNE”) and the currently played song name (“Habitt” by “Laurell”) in the cluster’s main display, audio tab. Note that the screen is switched back and forth multiple times and only radio-BAP-information is shown in this CAN-trace which covers round about 30 seconds in total. That is why you see multiple occurences of the station-information. It is because data is always transmitted as soon as the audio-tab is opened.

CAN-messageCAN-ID (hex)comment
BAP_Clima, BAP_ASG_05, BAP_ASG_06, BAP_RV, BAP_SWA, BAP_MDI, BAP_Telefon_01, BAP_Telefon_02, BAP_Telefon_03, BAP_ASG_03, BAP_Anzeige_02, BAP_ASG_04, BAP_RV, BAP_DSP, BAP_ASG_08, BAP_TV_Tuner?
list of known BAP-channels with corresponding CAN-ID

BAP communication

Inside the BAP system each control unit has (at least) one so called logical-ID “lsg_id” (logische Steuergeräte ID ~ logical control unit ID) which is partly derived from the CAN-message’s CAN-ID. Note that most control units have multiple lsg_ids, like telephone-menu and telephone-screen have different lsg_ids.

BAP_Charisma(*1)23 (0x17)
34 (0x22)
BAP_NAVI8 (0x8)
21 (0x15)
25 (0x19)
28 (0x1c)
35 (0x23)
50 (0x32)
BAP_AUDIOcluster shows RDS-information only, no menu ?49 (0x31)
BAP_SHZ(*1)3 (0x3)
BAP_Telefon_048 (0x8)
12 (0xc)
19 (0x13)
20 (0x14)
43 (0x2b)
BAP_RDKS(*1)4 (0x4)
7 (0x7)
8 (0x8)
12 (0xc)
BAP_ACC(*1)5 (0x5)
27 (0x1b)
BAP_OPS0 (0x0)
3 (0x3)
10 (0xa)
BAP_VZArecognized traffic signs in BAP-traffic33 (0x21)
BAP_LDW(*1)25 (0x19)
BAP_SWA(*1)26 (0x1a)
observed lsg_ids

*1 = menu items in cluster, at least no human readable content (like menu-item-textstrings) were observed in BAP-traffic from this unit

For each lsg_id exists a individual list of function-IDs “fct_id” (Funktions-ID ~ functional ID). These function-IDs describe functions that can be applied to the (sub-) control unit. For example: list all functions, screen-data, menu-data, … . For each lsg_id exists a special (device specific) set of fct_ids. However some fct_ids seem to be standardized in the BAP standard an thus often repeat:

0x01GetAllget all propertiesused to synchronize the application-cache in the ASG. FSG delivers all property-values with op_code ‘StatusAll’. Parameter ‘Data’ of op_code ‘StatusAll’ contains all function-property-values in one continously segmented BAP-message

op_code: GetAll (noParam), StatusAll (data), Error (errorCode)
0x02BAP-ConfigBAP_Version_major (byte), BAP_Version_minor (byte), LSG-Class (The LSG_Class_ID classifies the function catalogue uniquely because a function catalogue can occur several times with different LSG-IDs), LSG-Sub-Class, LSG-Version_major, LSG-Version_minorinformation for BAP-version and LSG-version

op_codes: Get (empty), Status (data), Reset (data), HeartbeatStatus (data),
Error (errorCode)
0x03Function-ListFctList = bitfield, bytestream,list of supported BAP-functions

This bitfield indicates the support of a certain function during runtime.

op_codes: Get (noParam), Status (FctList), HeartbeatStatus (FctList), Error (errorCode)
0x04HeartBeatHeartBeatTime (byte, scale=100, unit=ms, 0..25500ms)this message contains the time betweent two heartbeat-messages. The parameter ‘HeartBeatTime’ * 100ms = time in unit ‘milliseconds, ms’. If a heart beat error occurs for a BAP-connection, it is reported on function HeartBeat (0x04)

op_codes: Get (noParam), Status (HeartBeatTime), HeartbeatStatus (HeartBeatTime), Error (errorCode)
byte: 0x00=normalOperation, 0x01=offStandby, 0x02=reserved, 0x03=initializing, 0x04..0x0d=reserved,
0x0e=functionInactive, 0x0f=defective,
with the parameter ‘OP-State’ this function announces the operational state of the FSG

op_codes: Get (noParam), Status (OP_state), HeartbeatStatus (OP_state), Error (errorCode)

The op_code (operation code ID) indicates the type of operation which is to be applied to the “fct_id-lsg_id”-combination. This could be something like set-value, get-value, reset, …

Error0x00=noError, 0x01..0x3f=stdBAPerror, 0x40..0xff=reserved

VW requires:

  • All parameters transmitted via BAP shall be stored in non-volatile memory on the FSG to keep information during loss of power.
  • Function-IDs 1 to 4 shall not be evaluated by application if not otherwise specified

Note that each lsg_id has its own list of fct_ids and each fct_id has its own set of op_codes. All named above are (hopfully) BAP-standardized, not any more device specific or at least the same over all BAP-participants.

Control & buttons

With displaying data, there is a need to control menus, select entries, manipulate values, etc. All this is handled by the FSG (and not via BAP). Button presses from the MFL (Multifunktionslenkrad ~ multifunctional steering wheel) or steering column stalks (up/down + OK/Reset at the wiper control stalk) are passed to the control unit (FSG) which then updates the ASG’s display. It is not entirely clear at the moment if the cluster (as main display device) also handles menus and propagates selected items or adjusted values to other control units (FSGs) (on request?).

For the multifunctional steering wheel CAN-message “mMFL_Tasten” transports the pressed button in byte[0] of CAN-message: CAN-ID 1473 (dec) = 0x5c1 (hex), cycle 100ms, dlc=4

Button codes are as follows for “normal” multifunctional steering wheel:

0x0A10menu up / MFA arrow right
0x099menu down / MFA arrow left
0x2234MFA up
0x2335MFA down
0x2840MFA OK / Reset (long press)
0x2941MFA back
0x022skip+ / right
0x033skip- / left
0x066volume +
0x077volume –
0x2A42PTT / Microphone / Voicecontrol

However some more modern PQ cars have a so called “MQB-style” multifunctional steering wheel with a completely different button layout. (These also have the GRA / cruise control switches) in steering-wheel-buttons and not in the turn-indicator-stalk or 3rd stalk). For those steering wheels, the button codes are different:

0x1016volume +
0x1117volume –
0x1521skip right +
0x1622skip left –

If no multifunctional steering wheel is present, the car has buttons on the right side of the wiper stalk. Those would be “up” / “down” and “OK” ( / Reset if long pressed) which also allow for navigation through the menus. It’s most probable, that those are routed with the message mLSM_1 (CAN-ID: 705 (dec) = 0x2c1(hex) in bit LS1_MFA_Tasten from SMLS into the system.

VW CAN and LIN (PQ plattform)

Note that the messaging scheme for PQ vs. MQB vs. MLB (Audi only) are entirely different. Following are the VW version definitions for the different CAN-matrix (as VW names its CAN-version).

  • PQ: CAN matrix v4.5.x (4.x ?)
  • MQB: CAN matrix v5.x
  • MLB: CAN matrix v6.x

CAN bus

VW uses 11bit identifiers in broadcast-manner, 29bit are used for diagnostic/ISO-traffic but that is (obviously) only seen when sniffing CAN bus during a diagnostic session.

Not all bus are present in all PQ cars. Passat B6 for example only has CAN powertrain + diagnosis. Passat B7 has powertrain, infotainment, comfort, cluster, infotainment mandatory.

CAN bus requires 120Ohm termination on either side of the distributed CAN. VW does use following terminations:

CANspeedtermination 1termination 2
AFS private100 kBit/sAFS: 66OhmAFS module left & right (both 2.6kOhm)
powertrain / Antrieb500 kBit/sECU / Motorsteuergerät: 66OhmACC (3C0-radars, LIDAR), AFS, Airbag, ESP/ABS, DCC, PDC/PLA, EPS/Lenkung, CAN-Gateway, TCU/Getriebe, LWS/Lenkwinkelsensor, GearSelector/Wählhebel, Haldex:
all 2.6kOhm
EPB (if not on private CAN with ABS)
diagnosis500 kBit/sCAN-Gateway: 66OhmOBD-II connector
infotainment100 kBit/sCIB/car interface box, CAN-Gateway, Radio&Navigation, phone, SHZ/Standheizung:
all 560Ohm
compass, MDI/mobile device interface, rearset audio, TV-Tuner, soundsystem:
all 5.6kOhm
cluster / Kombiinstrument500 kBit/sCAN-Gateway: 66Ohmcluster: 2.6kOhm
comfort100 kBit/sBCM, CAN-Gateway, clima: 560Ohm
driver-door, passenger-door: 1kOhm
trailer, Multifunktionssteuergerät, SMLS/steering column electronics, memory driver seat:
all 5.6kOhm
EPB private / parking break 500 kBit/sEPBABS/ESP (not MK60EC1)
SWA / side assist private500 kBit/sside assist masterside assist slave
extended500 kBit/s? CAN-Gateway ?
probably 66Ohm ?
MFK/multifunctional camera, SWA/side assist, ACC (post 3C0-radars like 3AA, 7N0)

“private” indicates a private CAN between control units. Private means that the CAN is not routed to the CAN-Gateway.

some abbreviations are more German than others:

  • AFS = Advanced Frontlight System
  • DCC = Dynamic Chassis Control
  • TCU = Transmission Control Unit = GSG = Getriebesteuergerät
  • LWS = Lenkwinkelsensor = steering angle sensor
  • BCM = Body Control Module = BSG = Bordnetzsteuergerät
  • SMLS = Schaltermodul Lenksäule = steering column electronics
  • EPS = Electronic Power Steering
  • SWA = Spurwechselassistent = lane change assist
  • EPB = Electronic Parking Brake = elektronische Parkbremse

LIN bus

speed 19,2 kBit/s

BCM-LIN / BSG-LINRLS/rain-&lightsensor, front wiper
Tür-LIN / doorback doors
SMLS-LIN / steering wheel electronicsMFL/MFSW multifunctional steering wheel
DWA-LIN / alarmalarm horn, sensors overhead (?)
Dach-LIN / roof

Higher level protocols


DDP (probably not used in PQ, clusters with red color dot matrix display)


HTerm commands / CMD / special functions / delay / wait

HTerm (here 0.8.5) has some special functions which can be entered selecting “CMD” (marked yellow in screenshot) and typing.

In above screenshot you can see the characters “test” followed by <CR> being transmitted with 15ms delay between each char.

delay (e.g. between characters) in milliseconds (approx.). Accuracy might vary:


controlling pins:



multiple commands can be seperated using semicolon (;) e.g: dtr=1; rts=1; wait=20; dtr=0; rts=0; and can be mixed with ASCII/HEX/DEC/BIN according to the dropdown-box / combobox.

Octave cheatset

File opening

clear all;

File operations

To include a *.m file (like test.m) at the location in the file simply use the filename without *.m in the code. E.g. to include “test.m” simply put “test” into the code.

Matrices & Vectors

m x n = column x row = Zeile x Spalte

a = [1;2;3] # vector 3×1 (vertical)
b = [1,2,3] # 1×3 (horizontal)

a = [1;2;3] # vector 3×1 (vertical)
b = [1,2,3] # 1×3 (horizontal)

c = [

r3 = c(:3); # select 3rd row

Filter, find and concanate

b = find( c(:2)==8 ) # return all values from 2nd row of c where value is 8

C = [A,B] # horizontal_concanation
C = [A;B] # vertical concanation

for concanation can use:

horzcat (array1, array2, …, arrayN)
vertcat (array1, array2, …, arrayN)


fig1 = figure(1);
hold on;
title (“Titel”);
xlabel (“x-axis”);
ylabel (“y-axis”);
grid on;
grid minor on;
box on;

plot(x, y, “+”, “markersize”, 10, ‘color’, ‘red’); # markers only
# to add line connecting markers -> use linestyle

legend(‘S11′,’S22’, “location”, “southeast”)
hold off;

Vertical & Horizontal lines

plot([x,x],[0,8], “linestyle”, “–“); # vertical line at x
plot([0,8],[y,y], “linestyle”, “–“); # horizontal line a y

Plot to pdf export

print (fig1, “my_pdfname.pdf”, “-dpdflatexstandalone”);
system (“pdflatex my_pdfname”);
open my_pdfname.pdf


str_result = strcat(str1, str2);
str = num2str(my_number)

LinuxCNC and charge pump

In many cases of a CNC mill, the stepper motors are controlled using the parallel port of a computer (LPT port). The CNC software toggles the pins (step & direction) for each axis according to the needs of the desired toolpath (G-code). The LPT port solution is the most simple and cheap way. One problem is latency and jitter of the software pin toggle which must be performed in realtime (synchronizing of multiple axes). Usually, LinuxCNC will take care of this problem although the result is not optimal (performance problems etc.)

Another problem of the LPT port is the undefined state and behavior during the boot of the computer. If you switch on your CNC mill usually the stepper motor driver is immediately activated. During the boot sequence of the controlling computers, some LPT pins are toggled or are in an undefined state. This might result in undesired steps and thus undesired motion of the CNC mill. In order to prevent this behavior, some stepper motor controllers have a built-in charge pump.  This charge pump requires a  defined squarewave signal at a specific LPT pin to enable the stepper motor controller. If this charge pump signal is missing the stepper motor driver won’t perform any motion (steps). The signal is only generated when the CNC software (here LinuxCNC) is running properly and no emergency stop condition is met. During the boot of the operating system the LPT pins might toggle or be undefined but the condition for the charge pump good won’t be met and thus no steps will be performed.

I am using the “Tripple Beast” stepper motor controller by Benezan Electronics which has charge pump functionality but the information provided here applies analogous to other motor controllers, too. In order to use the charge pump (sometimes called watchdog) on the Tripple Beast jumper 2-1 needs to be removed. The square wave signal (10kHz … 100kHz) is expected on LPT pin 16. Please refer to the manual of your stepper motor driver for the exact specifications of your charge pump.

If you’re setting up LinuxCNC you probably will be using the “Stepconf Wizzard”. During setup, you can specify the output pin of the LPT port for the charge pump. In the drop-down menu of the pin (for me it’s pin 16) select “charge pump”. This will route the charge pump signal to the desired pin in the LinuxCNC HAL.

Unfortunately, some manual changes in the HAL file are required which can not be done in Stepconf Wizzard. Please note, that the following manual changes will be overwritten every time you perform Stepconf Wizzard! In general it’s strongly recommended to backup the settings files for each LinuxCNC profile before changes or Stepconf Wizzward. Probably a (local) git repository is a suitable solution (fast revert and good diffs due to text based settings).

  1. Navigate to the HAL file of your machine (*.hal) and open it with a text editor (e.g. “Mousepad” in LinuxCNC environment).
  2. Completely remove the line

net estop-out charge-pump.enable iocontrol.0.user-enable-out

  1. Add the following line instead

net estop-ext => charge-pump.enable

After that you should see a squarewave output on the desired pin (no emergency stop must be active in LinuxCNC). In my case (Tripple Beast stepper motor controller)  there is a “charge pump OK” LED which will light up green when a valid charge pump signal is detected.

Please note that the frequency of the charge pump signal (“charge-pump.out”) is the frequency of the LinuxCNC base thread. If the frequency is too high to meet the specifications of your charge pump you can use a divided by 2 (“charge-pump.out-2”) or divided by 4 (“charge-pump.out-4”) signal. (See LinuxCNC documentation). Frequencies higher than those of the base thread of course can not be achived.

rfft and irfft for Octave / Matlab

One-dimensional Discrete Fourier Transform (DFT) for real input.

function rfft = rfft(a)
ffta = fft(a);
rfft = ffta(1:(floor(length(ffta)/2)+1));

Inverse of the Discrete Fourier Transform (DFT) for real input.

function irfft = irfft(x,even=true)
n = 0; % the output length
s = 0; % the variable that will hold the index of the highest
% frequency below N/2, s = floor((n+1)/2)
if (even)
n = 2 * (length(x) – 1 );
s = length(x) – 1;
n = 2 * (length(x) – 1 )+1;
s = length(x);
xn = zeros(1,n);
xn(1:length(x)) = x;
xn(length(x)+1:n) = conj(x(s:-1:2));
irfft = ifft(xn);


GPS ceramic patch antenna (Aliexpress) S11

Some time ago I ordered a lot of (cheap) and small GPS patch antenna from Aliexpress.


I was very interested in the resonant frequency so I decided to connect it to my HP8753C to evaluate the match.


    • Minimum S11 -13.4 dB (VSWR: 1.544:1, reflection coefficient gamma: 0.214) at approx. 1070 MHz
    • Antenna resonant peak far off GPS L1 center frequency (nominal 1575.42 MHz). Usually, a sufficient ground plane is required for patch antennas, but these usually shift the resonant frequency to lower values.
    • Nevertheless the match looks better than 10dB at the 1575.42 MHz which is sufficient

Side Note: A good match does not necessarily lead to a “good antenna”. Although a good match is a basic requirement for all energy being transported from (and to) the antenna it is not guaranteed that the energy will be radiated in an effective way. S11 (or VSWR) only quantifies the amount of energy being reflected back from the antenna into the transmitter. It is up to the antenna (material and geometry) to efficiently radiate the delivered energy. Just think of a dummy load as an extreme of this phenomena: Nearly all energy is transferred (very good match, S11 minus many dB) but nearly nothing is radiated. Beside impedance mismatch losses, ohmic conduction losses (copper etc.) and mostly dielectric losses of surrounding material (like FR4 etc.) massively degrade antenna efficiency. The belonging measure is called “antenna efficiency” mostly given in %. (For RX operation the same applies analogous due to the antenna reciprocity theorem).


    • HEWLETT PACKARD 8753C, SW: 4.02
    • APC-7 – SMA (f)
    • SMA (f) – U.FL (m) MULTICOMP MC000984
    • RG 58A/U 0,5m SMA (m) – SMA (m)
    • Kirkby Microwave 85033, ‘50Ω SMA Calibration & verification kit’

Touchstone file (*.S1P)



Self-sustaining circuit using P-channel MOSFET

The circuit below shows a simple but effective way to power activate a (DC) device by pressing a button. The device can be completly switched of electronically (e.g. by a microcontroller). (Switch SW2 if only shown to demonstrate the operation principle of the circuit.)

Depending on the selected P-channel MOSFET T13 the circuit can handle voltages bigger of more than 60V and currents of several tens of amperes.

The same functionality could be achieved by a relay at the expense of volume (relay is much bigger) and power consumption (~100mW or more). Especially in a battery powered portable application, there is no way for a relay.

Circuit description

Power enters the schematic at the PWR_IN port (top left). In OFF-state the MOSFET handling the power (T13) is blocking current from its source to drain. Please note the body diode of the MOSFET is in reverse to the current flowing from PWR_IN to PWR_OUT when MOSFET is conducting. In the OFF-State the gate of T13 is pulled to the PWR_IN voltage level via pull-up resistor R294 thus keeping the MOSFET not conducting. The value of R294 is selected to keep safe operation at minimal losses when the gate is pulled low.

To activate the device the user will press the activation switch S3 which closes the contacts A-B and pulling the gate of T13 to GND. As soon as the gate-source voltage at the gate of the MOSFET drops below Vgs_th (threshold voltage see the datasheet of your preferred MOSFET) T13 will start conducting. As current flows from PWR_IN to PWR_OUT a microcontroller connected to PWR_EN can boot and then control the bipolar NPN transistor T14 which will pull the gate towards GND, too. As the microcontroller starts to work it immediately needs to output high level (3,3V or 5V) to T14. After this, the button can be released by the user. The sequence to power a microcontroller should take less than 1ms.

Remaining components are mostly protecting the MOSFET and the microcontroller pin. One big problem with this kind of circuit is the limited gate-source voltage of most modern MOSFET. The majority of them will take permanent damage if the gate-source voltage (Vgs) is bigger than (-)20V. If you want to use this circuit in a 24V or even 48V environment Vgs needs to be limited. This is done by the zener diode D111. Across D111 there is a constant voltage drop of the zener voltage (chosen to be 10V here). At this voltage, Vgs is big enough to keep T13 fully conducting. D111 forms a voltage divider in conjunction with R295. TVS diodes D110 and D112 further protect T13 against voltage transients and act as a basic over voltage circuit (requiring a fuse elsewhere before this circuit!). D113 is used to protect the bipolar NPN transistor T14 in the case of an overvoltage event. R296 acts as current limiting resistor into T14 in order to limit the current out of the microcontroller pin. For T14 any NPN (e.g. BC817, BC547, BC548, …) should be fine. TVS diode D114 limits the current in case of a failure at the MOSFET (low impedance failure MOSFET conducting input voltage to its gate).

In order to switch the device OFF the microcontroller needs to deactivate its output pin (switch to lo-state). As a consequence, T14 stops pulling the gate of T13 to GND. The gate is charged again to input-level voltage (via pull-up resistor R294). T13 stops conducting, the circuit’s power supply is cut off and so is the microcontroller. Nearly no current is consumed by this circuit in OFF-state.

Simplification for lower voltage

If you have a supply voltage which is lower than the maximum gate-source voltage (Vgs_max) of your selected MOSFET (T13 above) there is no need for the MOSFET driver (T14), the voltage divider and zener diodes. This might be useful at USB designs etc.

As you can see on the left side of the circuit above the self-sustaining circuit only consists of MOSFET T1, pull-up resistor R43 (and R18). R18 is not necessary but can protect the microcontroller pin connected to EN_MOSFET in the case of an overvoltage event.

Si(x) / Ci(x) (Sine integral function / Cosine integral function) in Python

The Si(x)-function (integral of the sine) in Python requires the usage of the scipy-package!


import scipy
from scipy.special import*

(si,ci) = sici(X)

Now si and ci hold the results of the sine / cosine integral.

Make sure not to confuse the Si(x)-function with si(x) = sin(x)/x = sinc(x), the cardinal sine function. Especially German literatur often uses the expression si(x) instead of sinc(x).