Python and C# – The best of both worlds

(Guest) Hinrich Rahlfs

25/10/2019

The aim of this article is to show how the .Net Framework and the Python libraries scipy and numpy can be combined effectively.

Anyone familiar with digital signal processing (DSV), will inevitably come into contact with Python or MatLab. I personally have been working with DSV in Python and feel very comfortable with it, especially because of the open source approach.
At Medtech Ingenieur, I started to get to grips with the .Net Framework (C#) and am fascinated by Visual Studio and its almost endless possibilities.
My project was and is to combine these two worlds: professional design of graphical user interfaces (GUI); comprehensive filter design.

In this post, I'll introduce a program we developed that uses the best of both worlds. I'll also provide a short guide that will allow you to use Python in your .Net application.

Robust, intuitive filter design

Creating and using filters with numpy and scipy is easy. Finding the "perfect" filter for your application is a much more difficult task. The program we developed allows you to try out different filter design methods without writing any code. It also provides useful information about each design method, allowing you to further develop your understanding of filter design and DSV to deepen.

Figure 1: ECG Signal superimposed with 50 Hz noise (SNR -10 dB)

In Figure 1, a very noisy ECG Signal loaded into the filter designer.

Figure 2: A band-stop filter is implemented using the filter designer

In the Filter Designer, you therefore create a band-stop filter. During the design process, you can view the filter's magnitude and phase response. The pole-zero diagram is also displayed. (Figure 2)
(Did you know, for example, that the poles of linear phase filters are always located at the origin; the zeros directly on the unit circle are responsible for the “notch”?)

Figure 3: Filtered signal

The ECG signal can now be clearly identified in Figure 3. Further filters could now be added, for example, to filter out the DC component.

Where are we and where will we go?

Currently, only the design of filters with linear phase is possible. These have a constant group delay. Therefore, the ECG The signal is still recognizable as such and is not subject to frequency-specific distortion. In the medium term, expanded options for designing recursive filters will be added.
In the future, it will also be possible to automatically generate C code that implements the created filters on a microcontroller.

If you are interested or have suggestions about which functions might be important for you, please feel free to contact me.

Instructions for your Python code in the .Net Framework

To start with: It could be so simple. You could simply use IronPython code (click here for an explanation). For that DSV However, since the two cPython libraries numpy and scipy are essential, the code cannot simply be executed in C#.

To use cPython, a backend.py file is developed. From this, a backend.exe is then built. This backend takes over all DSV Tasks and is executed by the C# code. The good thing about this solution is that it automatically forces you to separate the frontend (C#) and backend (Python).

The Python part

Since the Visual Studio debugger doesn't have the ability to debug the main.exe file, it's important to ensure that the Python code can be tested independently of the main application. For this purpose, it's recommended to query in the backend.py file at the beginning whether C# code or a person is running the application. This allows for debugging during development in the Python IDE of your choice.

It's important to note that backend.exe will be located in a different directory than backend.py. Therefore, everything that changes with the different call variants should be addressed at the beginning. At the end of backend.py, you can then execute what happens the same way for all call variants.
A corresponding code example can be found below. A Python file that does a little more is here to find.

if __name__ == "__main__": # Executed without arguments from the Python IDE # (or from the cmd with: python backend.py) if len(sys.argv) == 1: output_file = open(".\\data_transfer\\outputsignal.txt", "w") a = "no arguments" #called with .\dist\backend\backend.exe "write this text" elif sys.argv[1] == "write this text": output_file = open(".\\..\\..\\data_transfer\\outputsignal.txt", "w") a = sys.argv[1] #called with .\dist\backend\backend.exe "write 1" elif sys.argv[1] == "write 1": output_file = open(".\\..\\..\\data_transfer\\outputsignal.txt", "w") a = "1" called with incorrect arguments else: exit() the program code that runs the same for all branches output_file.write(a) output_file.close()

After successfully implementing the backend, pyinstaller can build an .exe from the .py file, which we can then execute from our C# code.

The C# part

The backend.exe created from backend.py can now be called with the following C# function. The arguments are passed and the program's stdout is returned.

 
/* @brief runs the command and returns the stdout as string @param cmd name of the program(backend.exe) @param args all args that are given to the backend */ protected string run_cmd(string cmd, string[] args) { ProcessStartInfo start = new ProcessStartInfo(); start.FileName = string.Format("{0}{1}", "..\\..\\..\\Python_Backend\\dist\\", cmd); string arguments = ""; //args need to be given in a single string (as you would type it to the cmd) foreach (string arg in args) { arguments += "\"" + arg + "\" "; } start.Arguments = arguments; // Any output, generated by application //will be redirected back start.RedirectStandardOutput = true; // Any error in standard error stream will //be redirected back (for example exceptions) start.RedirectStandardError = true; using (Process process = Process.Start(start)) { using (StreamReader reader = process.StandardOutput) { string stderr = process.StandardError.ReadToEnd(); string result = reader.ReadToEnd(); return result; } } }

The data transfer

There's one final problem to solve: transferring information between Python and C#.
What exactly backend.exe should do can be passed using the argv. Status updates and errors from backend.exe can be reported using the out and error channels. However, due to the limited length of the argv, data transmission must be handled using a different method. Signals can be stored as .csv files or serialized using .txt or .json files.

If you have any questions about the code examples, please feel free to contact me. If you have a complex C# program that could benefit from adding a Python backend, please feel free to contact us.


Hinrich Rahlfs worked as a student engineer at MEDtech. His responsibilities included programming graphical user interfaces and developing an intuitive filter design tool.


More articles

  • 09/09/2025
  • General, Software

In previous blog posts, I have introduced two essential components of a simple and universally applicable software architecture: events with dispatchers, listeners, and data pools. These already allow for many simple use cases ...

Read more
  • 12/11/2024
  • General, Software, Testing, Tools

In safety-critical software projects, software quality is paramount. Especially for Class C software, which must be certified according to strict standards such as IEC 62304 (medical technology), it is essential that ...

Read more
  • 08/08/2024
  • General, Electrical Stimulation, Software, Testing

Nowadays, apps in the healthcare sector are very important. Apps that can read and process data from medical sensors are particularly useful. Flutter is an open-source framework from Google that is excellent for ...

Read more
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.

Strictly Necessary Cookies

Strictly Necessary Cookie should be enabled at all times so that we can save your preferences for cookie settings.