 Yes, thank you for the presentation, Marco. This is going to be a quick demonstration of the API in the new version of the PSL Python library that was released earlier this year. So as you can see in the shared screen on the right-hand side, there is a live seed of my PSL board. Hello. So you can see what I'm doing. I will be connecting some wires and some resistors and stuff to it later. And on the left-hand side, there is a iPython shell, which I will be using to control the PSL. And I will try to go a bit slow so that you can hopefully follow along. But first things first, this is not an introduction to Python itself. So in order to follow along, it's good to have some familiarity with Python, the programming language already. Otherwise, it might be a bit difficult to follow. First thing we're going to look at how to do today is how to install PSL Python. A few months back, it is available through the Python package index, which means that we can install it using the install PSL. In my case, I already have it installed, of course, so that was very quick. But it should be basically the same for someone who does not already have it installed. The second step that is required for Linux users only is to allow your current user to access the serial interface to the PSLab device. Some Linux regular users typically do not have sufficient permissions to access serial devices unless they are members of the dial-out group. And in that case, you need to install something called a UW rule. And that can be installed by running PSLab install after you have installed it from a pip. So in my case, I have already done that, so it just tells me that that's already done and not necessary. You may need to run that as a route. Okay, once that is done, we can import PSLab. And then let's first take a look at the oscilloscope instrument. So first we need to connect to the PSLab board, which is done by instantiating the science lab class. You can see that the LED on the board turned green, which means that the connection was successful. So let's start by capturing an oscilloscope trace. So the science lab class is an aggregate which holds all of the other instruments. They can also be instantiated individually, but we're not going to cover that today. So the PSL object, which is a science lab instance, has an oscilloscope attribute, which we can use to capture an oscilloscope trace. The capture method takes the number of channels on which to capture samples as the first argument. So we will capture samples on one channel for now. The second argument is the number of samples to capture, which can be at most 10,000, which is what we're going to do right now. And the third argument is the time gap between each sample in microseconds. For now we are going to use one microsecond. You can see that the system LED on the PSLab blink, which means that it's performed some kind of action. So we're going to want to plot this, so let's import matplotlib to do that. We can plot the timestamps, which we got in the X variable against the voltage levels at those timestamps. And there we go. So as you can see, that was not very interesting because there's nothing connected to the board right now. So all of the samples are, of course, very close to zero. So what we're going to do is you're going to connect one of the analog outputs, specifically the one labeled SI1 to one of the analog inputs, specifically the one labeled CH1, channel 1. There we go. We can use the waveform generator instrument to generate an arbitrary waveform on the analog output pin that we just connected by calling the waveform generator attribute of the science lab instance, the generate method of that attribute. The generate method takes the number of channels on which to generate waveforms as the first argument or alternatively you can name a specific channel on which to generate the waveform. The second argument is the frequency of the waveform. So by default, the waveform that will be produced is a sine wave, which we will see in a moment. So now we are generating a sine wave on the analog output. Let's see if we can capture it on the analog input. So we will rerun the capture method. There we go. Let's plot that again. And there we go. That's much more interesting. We can see that we managed to capture a sinusoid. If we do the math on this, which I'm not going to do, we will find that the frequency is indeed one kilohertz. So that's capturing a single channel. You can also see that in this case the trace starts at an arbitrary voltage, which we might not always want. So if we rerun the capture command and plot it again, it will start at a different voltage. A common feature of oscilloscopes is the ability to trigger the capture at a specific voltage. And of course the PSLab can also do that. So we will add a trigger condition to the capture call. It will trigger at zero volts. When the signal passes zero volts from low to high, the capture will begin. I'm going to run that command twice because there's sometimes an intermittent bug that makes it not work the first time. There we go. Now we can see that the capture trace starts at zero volts. We can also trigger at a different voltage. We could trigger at 1.5 volts, for example. And then we see that the trace starts at 1.5 volts. We can also capture on multiple channels simultaneously. So I'm now going to connect another analog output to another analog input, specifically FI2 to CH2. Let's see that. So we will again call the waveform generator attribute and the generate method of that attribute. This time we will generate waveforms on two channels simultaneously. We will still use 1,000 hertz at the frequency. And we will offset these waveforms by 90 degrees. Now let's capture some traces again. So now we have to capture two traces. So we have to add another variable, start here, capture on two channels. And now we have to reduce the number of samples to capture because the total number of samples, which is the channels multiplied by the samples, must not be higher than 10,000. Okay. Let's take a look at that. We will plot X versus Y1 and X versus Y2 because the timestamps are of course shared between the samples. There we go. You can see that we are now capturing two sinusoids and they are indeed offset by 90 degrees. The analog or rather the arbitrary waveform generator can output waveforms other than sine waves as well, of course. So we will reconfigure one of the channels to instead output a triangle waveform. So then we call the waveform generator attribute and use the load function method, which takes the channel name on which to load a new function as the first argument and it takes the type of waveform as the second argument. So here I will be, here I'm loading a triangular waveform. So triangular waveform is predefined in the library. So we only have to specify that as a string. We will see later that we can also specify arbitrary functions. Let's start with this. Now the waveform on the second channel should be triangular, no longer sine and soil. Let's see if that is the case. Indeed, that worked. Fantastic. So you can see that the settings are otherwise preserved. The frequency and the phase are the same as before. It's only the shape that has changed. Let's see how we can load an arbitrary waveform into the other channel. So to do that I'm going to import numpy. If we can't load two superimposed sine waves into the first channel, to do that I'm going to import a lambda function to the load function method. So let's say that we want to pass the following function, sine of x plus a higher frequency sine of so that's on top of that. Then we also have to pass the span across which this function is defined. So we didn't have to do that when we loaded the triangular waveform because that is predefined in the library. But if you're loading an arbitrary waveform that is not predefined then you have to specify the period across which this function is defined. So we will define it over zero to two pi. Let's take a look at what that looks like. There we go. As you can see on the first channel we are now outputting two superimposed sinusoids. First there's the one kilohertz sinusoid as we saw before but there's also a five kilohertz sinusoid with a lower frequency superimposed on top of that sinusoid. So that's how you can load arbitrary waveforms into the PS lab. Let's take a look at if we can't load a square waveform and see what that looks like. To do that we will take the absolute value, rather we take x, and then we will divide x by the absolute value of x. So that will be minus one for negative values of x and one for positive values of x. We also have to consider zero. We don't want to divide by zero so we will add x equals zero here. So if x is equal to exactly zero this part here will be equal to one and otherwise it will be equal to zero. This is to avoid division by zero and we will define this on the span minus one to one. Then we will capture that and see what that looks like. So as you can see we are now outputting approximately square waveform. Since I did not multiply the function by three the amplitude of the waveform is now one to minus one. We can also increase the amplitude by multiplying this function by three, up to three. The voltage cannot exceed three volts, or 3.3 volts I think, like that, and there we go. Now we have a square waveform with a higher amplitude. There is also a different way to generate square waveforms using the PS lab and that is to use the PWM generator which is what we are going to look at next. So I am going to disconnect the first channel from the analog output and connect it to a digital output, specifically the digital output labeled SQ1. There we go. So now we will call the generate method of the PWM attribute of the science lab instance. We will generate on one channel, we will generate a waveform, the frequency of 1,000 hertz and a duty cycle of 50%. Let's take a look at that. All right. So now the triangular waveform disappeared which is because some of the digital outputs and analog outputs share hardware resources so we will actually disconnect that as well. We can see what's a bit more clearly and then we can just capture a single channel. We don't have to use the trigger anymore. So actually when you have set the trigger once, if you wanted to disable the trigger you call it with the false argument and there we go. As you can see the PWM generator of course outputs more clean square waveform than the analog output can do. It can also generate much higher frequencies than the analog output. The analog output, the analog waveform generator is limited to about 5,000 kilohertz, as we saw previously during the open tap presentation. But the PWM generator is limited to about 8 megahertz so quite a lot more. So we can set this to 1 megahertz. That was the wrong method, sorry. I won't do that with a telescope. Here we go. So we will see that if we try to do this, if we try to observe a 1 megahertz signal using the oscilloscope we won't get very good results because the oscilloscope is not fast enough to that. So that's where the logic analyzer instrument comes in. The logic analyzer instrument has an equivalent capture method. We will call PSL logic analyzer capture. We will capture on a single channel. That's enough for now. Wait, okay. So right now we didn't capture anything because we have to move the wire to the digital input. Then we can see that now we captured a bunch of stuff here. So these values that we have captured are time stamps. Time stamps where logic level events were detected. And the logic level events in this context means a transition from low to high or high to low. So in each of these time stamps the PSL detected that the logic level at the digital input transition from low to high or high to low. So in order to plot that, we can actually see what we're doing. First of all, I'm going to capture fewer samples because otherwise we're not going to see anything that captures 10 events. In order to actually plot that, we can call the helper function getXY like that. And then we can plot that just as we did the oscilloscope trace. There we go. The X axis here is microseconds. Actually, when did I start? How much time have I used? I lost track of time. I think I started at 12.15 roughly. So we're 15 minutes done. Is that correct? If you'd have like five minutes, is it? Five minutes? I think you started at 10, right? I started at 10 post, so 20 minutes. This is a 14 minute talk. Is that correct? So I have 20 minutes left. That's what the steps in the sketch. It's a 40 minutes talk. Good. I think I need to move a little bit faster here. Okay. We can also, of course, generate multiple PWM waveforms simultaneously. So now I'm going to connect a second digital output to a second digital input. There we go. Now we're going to generate PWM signals on two different pins. We can use different duty cycles for the two waveforms, but they must share the same frequency. So we will have the second waveform have a duty cycle of 25%. We can also offset them by a phase between 0 and 1. So we can offset them by, for example, 33%. And then we capture time stamps again. This time we will capture on two channels. Let's capture a few more time stamps. Now you will see that we have two arrays in the timestamp variable. As before, we can use the getXY function to get something that we can plot. Unlike the oscilloscope, the logic analyzer, the return values from the logic analyzer are not guaranteed to have the same time stamps. So here we have to use two different X variables. And then we can plot this as before. So in order to be able to see something, I mean it's going to offset the second trace by one volt. There we go. That seems a little strange. It seems like the duty cycle of the second waveform, which should be 25%, is instead 75%. Live debugging. Let's just skip that for now. So as I said, while the PWM wave signals can have individual duty cycles and phases, they must share the same frequency. But sometimes you might want to use two different PWM signals with different frequencies. And in that case, you can use the reference clock mapping method to generate the second signal. So we will do that on the second channel here. It happens. So we'll use the map reference clock method here. So we will do that on the second output. And this method takes a prescalar as the second argument. And the prescalar here, this method is a little bit tricky to use. So unlike the generate method, you don't set the frequency directly because you cannot set arbitrary frequencies using this method. You can only set frequencies that are even ratios between 128 megahertz and powers of 2 up to 15. So that means 128 megahertz, 64 megahertz, 32 megahertz, 16 megahertz, 8, 4, 2, 1, half a megahertz, and so on and so forth. And the prescalar is basically the denominator in that calculation. So if we set the prescalar of, for example, 8, we will get a frequency equal to 128 megahertz divided by 2 to the power of 8, which I believe is how much is that? That's half a kilohertz. No, half a megahertz, 500 kilohertz, sorry. And now we will see that we can capture two signals with different frequencies. So as you can see, the frequency on the second channel is now half of the frequency on the first channel, which as you may recall is 1 megahertz. So that's how you can generate PWM signals with different frequencies. Some other limitations of the reference block map is that the waveform or the signal is locked to 50% of the cycle. Okay, let's move on to the next instrument, which is the multimeter and the programmable power supply. I'm just going to connect one of the analog inputs to one of the programmable voltage sources. So now we're going to use programmable voltage source 1 is right there. The multimeter can be accessed as the other instruments as an attribute to the science lab instance. It has a measure voltage method, which by default measures the voltage on the pin labeled VOL, I think. So right now I have connected it to a different pin, CH1, so I'm just going to tell it to measure the voltage on that pin instead. Okay, minus 5 volts. That's probably correct. Let's see if we can change that. We can change that by calling the power supply attribute, and then we are now connected to the pin labeled PV1. We can set the voltage like this, and now hopefully we will measure zero volts. So PV1 can be set to any voltage between minus 5 and 5 volts. So if we set it to 5 volts and measure the voltage with the multimeter again, yes, we now get 5 volts. There are two other, the piece that has three programmable voltage sources. PV1, which we are using right now, can generate voltages between minus 5 and 5 volts. PV2 can generate voltages between minus 3.3 to 3.3 volts, and PV3 can generate voltages between zero and, I believe, 3.3 volts. Next, let's take a look at the programmable current source. So in order to see anything there, I'm going to connect an LED between the PCS pin and the ground pin. Hopefully you can see this. Now we are going to call the power supply attribute, and we are going to set a value on the PCS pin. The programmable current source can output currents between zero and 3.3 milliamps under ideal conditions, which do not always apply as we will see in a moment. Let's start by setting it to 1, let's start by setting it to half or a milliamp. Yes, I think you can see that the LED turned on. If we increase the current to 1 milliamp, we should see that the LED gets brighter. Which it did. It's fantastic. It can go a little higher, 1.1 milliamps or 1.5 milliamps, I mean. Now it's a dimmer instead. I'm not sure if you saw that. Let's do that again. That's 1 milliamp, and here we are trying to set 1.5 milliamps. But instead it gets dimmer, so the current clearly is lower. And the reason for that is, as I mentioned before, the programmable current source can output between zero and 3.3 milliamps under ideal conditions. But ideal conditions means a load resistance equal to zero. This is a red LED, which has a voltage drop of approximately 1.7 volts. Which means that the load resistance is equal to 1.7 volts divided by whatever current we are passing over the LED. And if the current is too high, the load resistance becomes too high and then the current source cannot output the requested current. So let's set it back to zero. I'm going to try to finish this up so we have time for some questions. Let's just show one more instrument, one more feature of the multimeter, which is the ability to measure resistances. So by connecting a resistor to move this wire, by connecting a resistor between the RES pin and a ground pin, we can measure the resistance of the resistor. So I'm not sure what type of the resistor this is actually. We will see in a minute. Again, we call the multimeter. This time we call the measure resistance method. And we see that, okay, this resistor has a resistance of approximately 1.16 ohms. No, what is that? 116 ohms, sorry. Yeah, okay. That's quickly about the multimeter. I think I'm going to skip the peripheral buses for now so that we have time for more questions and it can make up some time here. I will just briefly mention that the PS lab can also be extended with external sensors on any one of its peripheral buses, which are there's an I2C bus, there's an SPI bus, and there's a UART bus. I'm going to wrap up the live demo there. And yeah, I would be happy to answer any questions that might have cropped up during the demo. Great. Thank you very much, Alexander. Excellent job. Pretty impressive. There's a question. I don't know if you can go to the shared notes, but there's a question from Alvin that he's running to some problems when importing PS labs. And he's asking if he really needs the hardware to do that import. Yes. You do need to have hardware. You're not to import. You should be able to import the library without the hardware, but it won't do anything unless you have hardware. Let's take a look at this error. Okay. Module not found. Module named PS lab. It means to install PS lab, which seems absolutely correct. And then we go to no module named PS lab. Yeah, that's very strange. I don't have a good explanation for that. Right off the top of my head, my guess would be that you might be inside of a Python virtual environment, perhaps. Where you did not install PS lab. That would be my first guess. Yeah, maybe we need more details about the installation. I think we need more details on the installation to answer that. Second question. Do I need third? Oh, yeah. No, no. I just I was I wanted to point out like maybe the failure when you don't have the hardware is comes when the connection is done, right? Yes, exactly. You should be able to import the library without the hardware. Second question. Do I need the piece of hardware to run this? Yes. The library does not offer any hardware independent functionality. So you can't really do anything interesting with it unless you have an actual PS lab device. And I have I also have a question very quickly. Do you have you started working on this on 2026? Yes, if I started working, I joined the project in summer 2020. So there's this, there was a Python library before, right? Did you do you work on top of that? Or did you rewrite or? Yes, this is this is basically a complete rewrite of that library. So the so the version 1.0 of the Python, which was the version that existed when I joined the project. It was it had basically all of the same functionality that we saw today. But the API was a bit more, let's say, clunky, because the library at that point was designed to be used as a middleware between the desktop application, the graphical user interface and the board. It was not really meant to be used directly as a standalone library. Although, of course, it could be used like that. I remember I used that library and we even had problems for some Windows machines and macOS. Have you tried this one with Windows and macOS? With Windows, yes. With Mac, no. Okay. All right. And that's the last question. What do you think about OpenTap? I think that's very cool. I wasn't aware that they were using the new API. So I was very happy to see that they have found that and that they are finding it useful. Very cool. Yeah, that's great. Yeah. That's amazing. All right. Thank you very much, Alexander. Great job. Amazing. Very impressive. I can't wait to test it out on my BS lab.