Jump to content
  • 0

Pydwf how to read a voltage while it is given at the same time


Oznur Caliskan

Question

Hi everyone,

I need to automatize a test procedure. So, I would like to apply a voltage and read it at the same time to decide whether it passed or not. I wrote a code for applied&expected voltage output but can't read the voltage while Analog Discovery's W1&W2 jumpers are connected to the related pin. Are there ways to do it ?

I tried this:

                for j in range(len(analogOut.applied_TC)):
                    analogOut.voltage_DC = analogOut.applied_TC[j]
                    analogOut.amplitudeSet(CH1, analogOut.voltage_DC)
                    read_voltage_CH1 = analogOut.amplitudeGet(CH1)
                    expected_voltage_CH1 = analogOut.expected_TC[j]
                    if ((analogOut.voltage_DC * (1 - float(analogOut.tolerance_TC[j]))) <= read_voltage_CH1 <= (analogOut.voltage_DC * (1 + float(analogOut.tolerance_TC[j])))) and ((expected_voltage_CH1 * (1 - float(analogOut.tolerance_TC[j]))) <= read_voltage_CH1 <= (expected_voltage_CH1 * (1 + float(analogOut.tolerance_TC[j])))):
                        x += 1
                        print(f"\n Applied voltage:{analogOut.voltage_DC} and Read voltage:{read_voltage_CH1}, Expected voltage:{expected_voltage_CH1} are equal!")
                    else:
                        print(f"Applied voltage:{analogOut.voltage_DC} and Read voltage:{read_voltage_CH1}, Expected voltage:{expected_voltage_CH1} are NOT equal!!")
                if x == 3:
                    print("x:",x)
                    print("TEST PASSED")
                else:
                    print("x:", x)
                    print("TEST FAILED")

But analogOut.amplitudeGet(CH1) function doesn't read current voltage. (According to my verification activities)

I may need to use the analogIn.statusSample(channel_index) from this example: https://github.com/sidneycadot/pydwf/blob/master/source/pydwf-examples/AnalogInSimple.py Bu I couldnt use analogIn while I was using analogOut. Maybe I need to add this function to my AnalogOut class? :

def demo_analog_input_instrument_api_simple(analogIn):


    channel_count = analogIn.channelCount()
    if channel_count == 0:
        print("The device has no analog input channels that can be used for this demo.")
        return

    analogIn.reset()
    CH1 = 0

    while True:
        analogIn.status(False)
        print("analog input", ", ".join("channel {}: {:25.20f} [V]".format(
            channel_index, analogIn.statusSample(channel_index)) for channel_index in range(channel_count)))
        print("Offset:", analogIn.channelOffsetGet(CH1))
        time.sleep(0.010)

Thanks in advance

Link to comment
Share on other sites

16 answers to this question

Recommended Posts

  • 0

Hi @Oznur Caliskan

 

I see you're using pydwf. Great! I made that. Always nice to see it being used.

First: I am a bit surprised by your code, as your analogOut variable (which, I assume, is the device.analogOut variable you got from the DWF device you opened) appears to have members like applied_TC and expected_TC, which are not normally there. (They are not provided by pydwf).

It seems like you added these to your instances, and that can be made to work in Python, but it's generally a bad idea to alter classes and object instances that you get from libraries. If I were you, I would re-write your code to not do that.

Then, to your actual problem.

It is important to realize that the analogOut functionality is strictly for analog output. The analogOut.amplitudeGet() method will simply return the currently configured output-value for an output channel, and that's not what you want.

 

If you want to do analog input, i.e., read an analog voltage, you will have to use the functionality provided by device.analogIn, and make sure the input pin is properly connected to your test circuit. Then, indeed, you should be able to use analogIn.status() and analogIn.statusSample() to get a single voltage reading from the specified channel. That is the simplest way to get an analog input value, and indeed in simple use-cases that could be all you need.

You write:

> But I couldnt use analogIn while I was using analogOut.

You should be able to use both instruments just fine; it's probably a programming error. If you post a complete, self-contained program that shows what you tried, I can probably point out the issue. I cannot tell from code fragments alone.

> Maybe I need to add this function to my AnalogOut class

Here, too: I don't know what you mean by that unless you show me your complete code. It doesn't sound like something you should do -- as I said above, you shouldn't be adding anything to the AnalogOut class, that class is managed by pydwf and you should just call its methods, not add methods to it or anything like that.

Cheers,
  Sidney

 

Link to comment
Share on other sites

  • 0
27 minutes ago, reddish said:

Hi @Oznur Caliskan

 

I see you're using pydwf. Great! I made that. Always nice to see it being used.

First: I am a bit surprised by your code, as your analogOut variable (which, I assume, is the device.analogOut variable you got from the DWF device you opened) appears to have members like applied_TC and expected_TC, which are not normally there. (They are not provided by pydwf).

It seems like you added these to your instances, and that can be made to work in Python, but it's generally a bad idea to alter classes and object instances that you get from libraries. If I were you, I would re-write your code to not do that.

Then, to your actual problem.

It is important to realize that the analogOut functionality is strictly for analog output. The analogOut.amplitudeGet() method will simply return the currently configured output-value for an output channel, and that's not what you want.

 

If you want to do analog input, i.e., read an analog voltage, you will have to use the functionality provided by device.analogIn, and make sure the input pin is properly connected to your test circuit. Then, indeed, you should be able to use analogIn.status() and analogIn.statusSample() to get a single voltage reading from the specified channel. That is the simplest way to get an analog input value, and indeed in simple use-cases that could be all you need.

You write:

> But I couldnt use analogIn while I was using analogOut.

You should be able to use both instruments just fine; it's probably a programming error. If you post a complete, self-contained program that shows what you tried, I can probably point out the issue. I cannot tell from code fragments alone.

> Maybe I need to add this function to my AnalogOut class

Here, too: I don't know what you mean by that unless you show me your complete code. It doesn't sound like something you should do -- as I said above, you shouldn't be adding anything to the AnalogOut class, that class is managed by pydwf and you should just call its methods, not add methods to it or anything like that.

Cheers,
  Sidney

 

Hi @reddish,

First of all, thank you for providing us this library, it is very user-friendly! Helps me a lot.. I had just a little problem.

Additionally, sorry for the confusion. Actually, my "analogOut" is just a parameter of my own class. I will provide my code to you and I am sure you will get what I am trying to do:

#! /usr/bin/env python3

"""Demonstrate the simplest way to control the AnalogOut instrument."""
import json
import time
import math
import argparse
import keyboard
import matplotlib.pyplot as plot
from datetime import datetime
import numpy as np
from matplotlib import pyplot
from matplotlib.animation import FuncAnimation
from operator import itemgetter #listeden item çekmek için
from pydwf import DwfLibrary, DwfAnalogOutNode, DwfAnalogOutFunction, DwfAnalogOutIdle, PyDwfError, DwfAnalogInTriggerLengthCondition, DwfAnalogInTriggerType
from pydwf.utilities import openDwfDevice
from keyboard import press
from GUI import AnalogInSimple

def demo_analog_input_instrument_api_simple(analogIn):


    channel_count = analogIn.channelCount()
    if channel_count == 0:
        print("The device has no analog input channels that can be used for this demo.")
        return

    analogIn.reset()
    CH1 = 0

    while True:
        analogIn.status(False)
        print("analog input", ", ".join("channel {}: {:25.20f} [V]".format(
            channel_index, analogIn.statusSample(channel_index)) for channel_index in range(channel_count)))
        print("Offset:", analogIn.channelOffsetGet(CH1))
        time.sleep(0.010)

class AnalogOut:

    def __init__(self, procedure, listof_cases, listof_content, applied, expected, tolerance, voltage_DC, verification_method):

        self.procedure = procedure
        self.listof_cases = listof_cases
        self.listof_content = listof_content
        self.applied = applied
        self.expected = expected
        self.tolerance = tolerance
        self.applied_TC = applied_TC
        self.expected_TC = expected_TC
        self.tolerance_TC = tolerance_TC
        self.voltage_DC = voltage_DC
        self.verification_method = verification_method

    def demo_simple_analog_out(analogOut):
        parser = argparse.ArgumentParser(description="Demonstrate simple usage of the AnalogOut functionality.")
        parser.add_argument(
            "-sn", "--serial-number-filter",
            type=str,
            nargs='?',
            dest="serial_number_filter",
            help="serial number filter to select a specific Digilent Waveforms device"
        )
        args = parser.parse_args()
        try:
            dwf = DwfLibrary()
            with openDwfDevice(dwf, serial_number_filter=args.serial_number_filter) as device:
                t0 = time.monotonic()

                """analogOut.signal_type = input("Please select the Signal Type:\n1. Press 1 for Sine\n2. Press 2 for DC\n3. Press 3 for Square\n")
                print("Type:", analogOut.signal_type, "\n")"""

                # Reset all channels.
                analogOut.reset(-1)

                CH1 = 0
                CH2 = 1

                for channel_index in (CH1, CH2):
                    analogOut.nodeFunctionSet(channel_index, DwfAnalogOutNode.Carrier, DwfAnalogOutFunction.Square)
                    analogOut.idleSet(channel_index, DwfAnalogOutIdle.Initial)
                    analogOut.nodeEnableSet(channel_index, DwfAnalogOutNode.Carrier, True)
                    analogOut.configure(channel_index, True)  # Necessary to give sine wave configured

                print("Processing...\n")
                with open("signal_procedure.json", "r") as file:
                    analogOut.procedure = json.load(file)
                    print(type(analogOut.procedure))
                print("There are", len([_ for _ in analogOut.procedure.keys() if "Test Case" in _]), "test cases.")
                #Getting test cases
                analogOut.listof_cases = []
                index = 0
                for index in analogOut.procedure.keys():
                    analogOut.listof_cases.insert(len(analogOut.listof_cases), index)
                #getting values of the test case dictionary
                analogOut.listof_content = []
                index = 0
                for index in analogOut.procedure.values():
                    analogOut.listof_content.insert(len(analogOut.listof_content), index)

                analogOut.verification_method = {}
                for k, v in analogOut.procedure.items():
                    analogOut.verification_method[k] = [analogOut.listof_content.get("Verification Method") for analogOut.listof_content in v]

                print("Verification methods:", analogOut.verification_method)
                print("*************")
                analogOut.applied = {}
                for k, v in analogOut.procedure.items():
                    analogOut.applied[k] = [[analogOut.listof_content.get("Applied Voltage") for analogOut.listof_content in analogOut.listof_content["Sub_Tests"]] for analogOut.listof_content in v]

                print("Applied voltages:", analogOut.applied)
                print("*************")
                analogOut.expected = {}
                for k, v in analogOut.procedure.items():
                    analogOut.expected[k] = [[analogOut.listof_content.get("Expected Result") for analogOut.listof_content in analogOut.listof_content["Sub_Tests"]] for
                                  analogOut.listof_content in v]

                print("Expected results:", analogOut.expected)
                print("*************")
                analogOut.tolerance = {}
                for k, v in analogOut.procedure.items():
                    analogOut.tolerance[k] = [[analogOut.listof_content.get("Tolerance") for analogOut.listof_content in analogOut.listof_content["Sub_Tests"]] for
                                   analogOut.listof_content in v]

                print("Tolerance values:", analogOut.tolerance)

                test_case_count = test_step_count = test_subtest_count = 0
                for test_case_key, test_case in analogOut.procedure.items():
                    if test_case_key.startswith("Test Case "):
                        test_case_count += 1
                        for test_step in test_case:
                            test_step_count += 1
                            print("****", test_step["Test Case"], test_step["Test Step"])
                            for sub_test in test_step["Sub_Tests"]:
                                test_subtest_count += 1
                                print(sub_test["Applied Voltage"], sub_test["Expected Result"],
                                      sub_test["Tolerance"])
                print("test_case_count:", test_case_count, "\ntest_step_count:", test_step_count,"\ntest_subtest_count:", test_subtest_count)

                cases = list(analogOut.applied.keys())
                print(cases)
                analogOut.results = []
                for key, value in analogOut.applied.items():
                    for i in range(0,19):
                        x = 0
                        print("*********", str(cases[i]), "*********")
                        analogOut.applied_TC = []
                        analogOut.applied_TC.insert(len(analogOut.applied.get(cases[i])), analogOut.applied.get(str(cases[i])))
                        analogOut.applied_TC = [float(eleman) for inner_list in analogOut.applied_TC for middle_list in inner_list for
                                    eleman in middle_list]

                        analogOut.expected_TC = []
                        analogOut.expected_TC.insert(len(analogOut.applied.get(cases[i])), analogOut.expected.get(str(cases[i])))
                        analogOut.expected_TC = [float(eleman) for inner_list in analogOut.expected_TC for middle_list in inner_list for
                                    eleman in middle_list]

                        analogOut.tolerance_TC = []
                        analogOut.tolerance_TC.insert(len(analogOut.applied.get(cases[i])), analogOut.tolerance.get(str(cases[i])))
                        analogOut.tolerance_TC = [float(eleman) for inner_list in analogOut.tolerance_TC for middle_list in inner_list for
                                    eleman in middle_list]

                        print("Applied values", analogOut.applied_TC)
                        print("Expected values", analogOut.expected_TC)
                        print("Tolerance values", analogOut.tolerance_TC)

                        for j in range(len(analogOut.applied_TC)):
                            analogOut.voltage_DC = analogOut.applied_TC[j]
                            analogOut.amplitudeSet(CH1, analogOut.voltage_DC)
                            read_voltage_CH1 = demo_analog_input_instrument_api_simple(analogIn="") #HERE I AM TRYING TO RECALL STATUS READ FUNC. BUT CANNOT
                            expected_voltage_CH1 = analogOut.expected_TC[j]
                            if ((analogOut.voltage_DC * (1 - float(analogOut.tolerance_TC[j]))) <= read_voltage_CH1 <= (analogOut.voltage_DC * (1 + float(analogOut.tolerance_TC[j])))) and ((expected_voltage_CH1 * (1 - float(analogOut.tolerance_TC[j]))) <= read_voltage_CH1 <= (expected_voltage_CH1 * (1 + float(analogOut.tolerance_TC[j])))):
                                x += 1
                                print(f"\n Applied voltage:{analogOut.voltage_DC} and Read voltage:{read_voltage_CH1}, Expected voltage:{expected_voltage_CH1} are equal!")
                            else:
                                print(f"Applied voltage:{analogOut.voltage_DC} and Read voltage:{read_voltage_CH1}, Expected voltage:{expected_voltage_CH1} are NOT equal!!")
                        if x == 3:
                            print("x:",x)
                            result = "TEST PASSED"
                            print(result)
                            analogOut.results.append(result)
                        else:
                            if analogOut.applied_TC == [] and analogOut.expected_TC == [] and analogOut.tolerance_TC == []:
                                continue
                            else:
                                print("x:", x)
                                result = "TEST FAILED"
                                print(result)
                                analogOut.results.append(result)

                    print("Results:", analogOut.results)

                    t_stopwatch = 0.0
                    counter = 0
                    t0 = time.monotonic()
                    while True:
                        t = time.monotonic() - t0
                        counter += 1
                        if counter == 500:
                            for channel_index in (CH1, CH2):
                                duration = (t - t_stopwatch)  # pylint: disable=superfluous-parens
                                print("{:8.3f} loops/sec. Press Q to quit.".format(1+counter / 1+duration))
                                print("Giving output for Channel", CH1, "is:", analogOut.amplitudeGet(CH1))
                            counter = 0
                            t_stopwatch = t

def main():
    """Parse arguments and start demo."""

    parser = argparse.ArgumentParser(description="Demonstrate simple usage of the AnalogOut functionality.")

    parser.add_argument(
            "-sn", "--serial-number-filter",
            type=str,
            nargs='?',
            dest="serial_number_filter",
            help="serial number filter to select a specific Digilent Waveforms device"
        )

    args = parser.parse_args()

    try:
        dwf = DwfLibrary()
        with openDwfDevice(dwf, serial_number_filter=args.serial_number_filter) as device:
            AnalogOut.demo_simple_analog_out(device.analogOut)
            demo_analog_input_instrument_api_simple(device.analogIn)
    except PyDwfError as exception:
        print("PyDwfError:", exception)
    except KeyboardInterrupt:
        print("Keyboard interrupt, ending demo.")


if __name__ == "__main__":
    main()

 

So as I said earlier, I cannot get input values analogIn.statusSample(channel_index) because I am already in output mode if I am getting it right ?

Link to comment
Share on other sites

  • 0

Hi @Oznur Caliskan

Wow, that was indeed confusing, because the "AnalogOut" name is also a class name of something used in pydwf.

It is not a very good name of your class anyway, because it doesn't describe what the class does or is, at all. I would recommend renaming it to HardwareTest or something like that, which is much more descriptive. Better yet: don't make it a class at all ! See below.

What seems to have happened in your code is that you have tried to paste two pydwf demo programs into a single program, then hope for the best that it works.... and then it doesn't. I think it would be much better if you study those demo programs and understand how they work, and then write your own program based on that. Throwing things together without proper understanding what you're doing is not a good way to write a reliable computer program.
 

Here's a few things that need to change in your program to make it better:

- I don't see the point of making a separate class for what you are trying to do. Why don't you make it a function perform_hardware_test() instead? It will simplify your code a lot. You should only use classes for things that need persistent state, and your class doesn't need that at all.

- The parsing of command line arguments in the demo_simple_analog_out method doesn't belong there. Argument parsing should happen in main(), or in a dedicated function called by main().

- The instantiation of the DwfLibrary and the opening of the DwfDevice should not happen in the demo_simple_analog_out method. I recommend that you do that in the main() function too, then pass the device that you get to perform_hardware_test() function as a function parameter.

Then, in that function, you can access both the device.analogIn and device.analogOut members, and do what you need to do.


TLDR: you really need to improve the organization of your program, and understand what you're doing and why. Don't try to just get away with just pasting two programs together; that's a recipe for failure.

Edited by reddish
Link to comment
Share on other sites

  • 0
3 hours ago, Oznur Caliskan said:

I would like to apply a voltage and read it at the same time to decide whether it passed or not.

For what it's worth, this is my reaction to the statement above.

Changing a voltage source and expecting it to settle to another level instantaneously is not a good idea, even if the output is a fast DAC plus some analog circuitry. This is especially true if reading the actual value involves some analog circuitry plus an ADC. So, some settling time is required to determine if the desired change happened and is stable. In practical terms a couple of ADC samples is required.

The question is whether or not the interface is slow enough to provide a time lag between setting the DAC, having the output buffer settle, and obtaining a new ADC sample. I don't know the answer to that question, but as a general rule adding some short delay between setting an output of a voltage source and reading the input of the same source makes sense to me.

However you do this, you need a good scope with a very high analog bandwidth to verify slew rates. Even then actual performance will depend on the input impedance of the load on the voltage source if it's not just the ADC input circuitry.

Conversion between analog an digital realms is not a trivial endeavor.

Edited by zygot
Link to comment
Share on other sites

  • 0
22 hours ago, reddish said:

Hi @Oznur Caliskan

Wow, that was indeed confusing, because the "AnalogOut" name is also a class name of something used in pydwf.

It is not a very good name of your class anyway, because it doesn't describe what the class does or is, at all. I would recommend renaming it to HardwareTest or something like that, which is much more descriptive. Better yet: don't make it a class at all ! See below.

What seems to have happened in your code is that you have tried to paste two pydwf demo programs into a single program, then hope for the best that it works.... and then it doesn't. I think it would be much better if you study those demo programs and understand how they work, and then write your own program based on that. Throwing things together without proper understanding what you're doing is not a good way to write a reliable computer program.
 

Here's a few things that need to change in your program to make it better:

- I don't see the point of making a separate class for what you are trying to do. Why don't you make it a function perform_hardware_test() instead? It will simplify your code a lot. You should only use classes for things that need persistent state, and your class doesn't need that at all.

- The parsing of command line arguments in the demo_simple_analog_out method doesn't belong there. Argument parsing should happen in main(), or in a dedicated function called by main().

- The instantiation of the DwfLibrary and the opening of the DwfDevice should not happen in the demo_simple_analog_out method. I recommend that you do that in the main() function too, then pass the device that you get to perform_hardware_test() function as a function parameter.

Then, in that function, you can access both the device.analogIn and device.analogOut members, and do what you need to do.


TLDR: you really need to improve the organization of your program, and understand what you're doing and why. Don't try to just get away with just pasting two programs together; that's a recipe for failure.

Hi again @reddish,

Thank you for your comments... They led me to simplify my code and it got easier to understand the concept.

According to the examples, am I going to use "read_voltage_CH1 = input.statusSample(CH1)" to read the voltage? Or maybe I couldn't set the voltage properly or maybe it cannot read the voltage because it can't keep up with the set voltage should I put sleep() between them?

Additionally, is it important which Digilent Device I am using? I am using Analog Discovery 2 and I am connecting both Wavegen and Scope cables to the related pin.

Here I will show you my code:

def parse_test_procedure(output,input):
    CH1 = 1
    CH2 = 2

    print("Processing...\n")
    with open("signal_procedure.json", "r") as file:
        procedure = json.load(file)
        print(type(procedure))
    print("There are", len([_ for _ in procedure.keys() if "Test Case" in _]), "test cases.")
    #Test Case List
    listof_cases = []
    index = 0
    for index in procedure.keys():
        listof_cases.insert(len(listof_cases), index)
    #Test Infos List
    listof_content = []
    index = 0
    for index in procedure.values():
        listof_content.insert(len(listof_content), index)
    #Verification Method List
    verification_method = {}
    for k, v in procedure.items():
        verification_method[k] = [listof_content.get("Verification Method") for listof_content in v]

    print("Verification methods:", verification_method)
    print("*************")

    #Applied Voltage List
    applied = {}
    for k, v in procedure.items():
        applied[k] = [[listof_content.get("Applied Voltage") for listof_content in listof_content["Sub_Tests"]] for listof_content in v]

    print("Applied voltages:", applied)
    print("*************")

    #Expected Results List
    expected = {}
    for k, v in procedure.items():
        expected[k] = [[listof_content.get("Expected Result") for listof_content in listof_content["Sub_Tests"]] for
                      listof_content in v]

    print("Expected results:", expected)
    print("*************")

    #Tolerance Value List
    tolerance = {}
    for k, v in procedure.items():
        tolerance[k] = [[listof_content.get("Tolerance") for listof_content in listof_content["Sub_Tests"]] for
                       listof_content in v]

    print("Tolerance values:", tolerance)

    # Counting Test Case, Test Step, Subtests
    test_case_count = test_step_count = test_subtest_count = 0
    for test_case_key, test_case in procedure.items():
        if test_case_key.startswith("Test Case "):
            test_case_count += 1
            for test_step in test_case:
                test_step_count += 1
                for sub_test in test_step["Sub_Tests"]:
                    test_subtest_count += 1
    print("Test case count:", test_case_count, "\nTest step count:", test_step_count,
          "\nTest subtest count:", test_subtest_count)

    # Test Cases List
    cases = list(applied.keys())
    print(cases)
    results = []

    for key, value in applied.items():
        for i in range(0, 19):
            input.reset()
            output.reset(CH1)
            x = 0
            applied_TC = []
            expected_TC = []
            tolerance_TC = []
            print("*********", str(cases[i]), "*********")
            applied_TC.insert(len(applied.get(cases[i])), applied.get(str(cases[i])))
            applied_TC = [float(eleman) for inner_list in applied_TC for middle_list in inner_list for
                          eleman in middle_list]

            expected_TC.insert(len(applied.get(cases[i])), expected.get(str(cases[i])))
            expected_TC = [float(eleman) for inner_list in expected_TC for middle_list in inner_list for
                           eleman in middle_list]

            tolerance_TC.insert(len(applied.get(cases[i])), tolerance.get(str(cases[i])))
            tolerance_TC = [float(eleman) for inner_list in tolerance_TC for middle_list in inner_list for
                            eleman in middle_list]

            print("Applied values", applied_TC)
            print("Expected values", expected_TC)
            print("Tolerance values", tolerance_TC)

            for j in range(len(applied_TC)):
                voltage_DC = applied_TC[j]

                output.nodeFunctionSet(CH1, DwfAnalogOutNode.Carrier, DwfAnalogOutFunction.Square)
                output.idleSet(CH1, DwfAnalogOutIdle.Initial)
                output.nodeEnableSet(CH1, DwfAnalogOutNode.Carrier, True)
                output.configure(CH1, True)
                output.amplitudeSet(CH1, voltage_DC)
                read_voltage_CH1 = input.statusSample(CH1) #DOESNT READ THE VOLTAGE
                expected_voltage_CH1 = expected_TC[j]
                if ((voltage_DC * (1 - float(tolerance_TC[j]))) <= read_voltage_CH1 <= (
                        voltage_DC * (1 + float(tolerance_TC[j])))) and (
                        (expected_voltage_CH1 * (1 - float(tolerance_TC[j]))) <= read_voltage_CH1 <= (
                        expected_voltage_CH1 * (1 + float(tolerance_TC[j])))):
                    x += 1
                    print(
                        f"\n Applied voltage:{voltage_DC} and Read voltage:{read_voltage_CH1}, Expected voltage:{expected_voltage_CH1} are equal!")
                else:
                    print(
                        f"Applied voltage:{voltage_DC} and Read voltage:{read_voltage_CH1}, Expected voltage:{expected_voltage_CH1} are NOT equal!!")
            if x == 3:
                print("x:", x)
                result = "TEST PASSED"
                print(result)
                results.append(result)
            else:
                if applied_TC == [] and expected_TC == [] and tolerance_TC == []:
                    continue
                else:
                    print("x:", x)
                    result = "TEST FAILED"
                    print(result)
                    results.append(result)

        print("Results:", results)

        """t_stopwatch = 0.0
        counter = 0
        t0 = time.monotonic()
        while True:
            t = time.monotonic() - t0
            counter += 1
            if counter == 500:
                for channel_index in (CH1, CH2):
                    duration = (t - t_stopwatch)  # pylint: disable=superfluous-parens
                    print("{:8.3f} loops/sec. Press Q to quit.".format(1 + counter / 1 + duration))
                    print("Giving output for Channel", CH1, "is:", analogOut.amplitudeGet(CH1))
                counter = 0
                t_stopwatch = t"""

def main():

    #parser operations must be done in main()
    parser = argparse.ArgumentParser(description="Demonstrate simple usage of the AnalogOut functionality.")
    parser.add_argument(
            "-sn", "--serial-number-filter",
            type=str,
            nargs='?',
            dest="serial_number_filter",
            help="serial number filter to select a specific Digilent Waveforms device"
        )
    args = parser.parse_args()

    try:
        dwf = DwfLibrary()
        with openDwfDevice(dwf, serial_number_filter=args.serial_number_filter) as device:
            parse_test_procedure(device.analogOut,device.analogIn)

    except PyDwfError as exception:
        print("PyDwfError:", exception)
    except KeyboardInterrupt:
        print("Keyboard interrupt, ending demo.")


if __name__ == "__main__":
    main()

Best,

Öznur :)

Link to comment
Share on other sites

  • 0
22 hours ago, zygot said:

For what it's worth, this is my reaction to the statement above.

Changing a voltage source and expecting it to settle to another level instantaneously is not a good idea, even if the output is a fast DAC plus some analog circuitry. This is especially true if reading the actual value involves some analog circuitry plus an ADC. So, some settling time is required to determine if the desired change happened and is stable. In practical terms a couple of ADC samples is required.

The question is whether or not the interface is slow enough to provide a time lag between setting the DAC, having the output buffer settle, and obtaining a new ADC sample. I don't know the answer to that question, but as a general rule adding some short delay between setting an output of a voltage source and reading the input of the same source makes sense to me.

However you do this, you need a good scope with a very high analog bandwidth to verify slew rates. Even then actual performance will depend on the input impedance of the load on the voltage source if it's not just the ADC input circuitry.

Conversion between analog an digital realms is not a trivial endeavor.

Hi @zygot,

Thank you for the heads-up. 

I use Analog Discovery 2 for this way of supplying/reading voltage to my development board. I agree with what you said, timing is important I can add sleep(). Do you think Analog Discovery 2 is not suitable for this application? What would you recommend?

Best,

Öznur

Link to comment
Share on other sites

  • 0

Hi @Oznur Caliskan

The code is in much better shape now, in terms of structure. Good.

The only reason your code doesn't work now, I think, is that you removed the input.status(False) before you read the input voltage. Put it back in (right before the statusSample call), and you should be golden.

The status() call command asks the device (via USB) for its most recent status, and, as part of the response, the device will also report the most recent sample value. This is the most basic way to get an analog input reading, and it could be sufficient for your application.

If you want to do fancier stuff, you could acquire a number of samples. This would allow, for example, to see the transient behavior of your device-under-test as you change the applied voltage; or you could get a bunch of samples to average them, which gives you higher accuracy. But it could well be that the accuracy is already plenty for whatever it is you're doing. The AD2 and AD3 have plenty of tricks up their sleeve beyong this basic usage.

Once you get it working (should be easy now), I would recommend that you put a few calls in your inner measurement loop outside of the loop. In particular, these four:

output.nodeFunctionSet(CH1, DwfAnalogOutNode.Carrier, DwfAnalogOutFunction.Square)
output.idleSet(CH1, DwfAnalogOutIdle.Initial)
output.nodeEnableSet(CH1, DwfAnalogOutNode.Carrier, True)
output.configure(CH1, True)

it's a bit cleaner to my eye to only do those once, effectively puting the analog output in a mode where the output voltage should immediately follow the output.amplitudeSet(CH1, value). It's a bit faster and cleaner to lift those calls out of the inner loop. But otherwise, a good step forward compared to your earlier version.

Last thing: while the analog input and analog output should work fine, it may be useful to verify their behavior against a properly calibrated desktop multimeter at some point. This gives you an indication of the systematic error you incur due to the calibration of the AD2/AD3 being off. It's probably not an issue, but it's always useful to do little sanity checks like that.

 

 

Link to comment
Share on other sites

  • 0
2 hours ago, Oznur Caliskan said:

I use Analog Discovery 2 for this way of supplying/reading voltage to my development board. I agree with what you said, timing is important I can add sleep(). Do you think Analog Discovery 2 is not suitable for this application? What would you recommend?

Part of the debugging process is trying to figure out why you aren't getting expected results. It could be your coding implementation or it could be that your design doesn't understand how the hardware works well enough.

I don't see any harm in putting a 100 ms sleep() delay between the lines where you set the DAC and read the ADC. If that produces results that are more like you expect than you've learned something. You can always tweak the delay to be as short as possible. You can also read the last voltage by getting an ADC sample prior to setting the next one. This is a bit more complicated but might result in a much faster test.

As to whether or not the AD2 is suitable for your application.. I don't know enough about your application to form an opinion.

Generally, expecting some delay between when a voltage source is commanded to change the output level and when it settles on a new value is important to keep in mind. It's possible that due to the timing of transmitting commands to your test hardware, the AD2, makes that update/settle time irrelevant. From the information that you've provided so far, your approach suggests that it isn't irrelevant.

Adding delays between test steps is easy enough to add, and remove later if there's no effect on results.

Edited by zygot
Link to comment
Share on other sites

  • 0
On 8/23/2023 at 6:36 PM, reddish said:

Hi @Oznur Caliskan

The code is in much better shape now, in terms of structure. Good.

The only reason your code doesn't work now, I think, is that you removed the input.status(False) before you read the input voltage. Put it back in (right before the statusSample call), and you should be golden.

The status() call command asks the device (via USB) for its most recent status, and, as part of the response, the device will also report the most recent sample value. This is the most basic way to get an analog input reading, and it could be sufficient for your application.

If you want to do fancier stuff, you could acquire a number of samples. This would allow, for example, to see the transient behavior of your device-under-test as you change the applied voltage; or you could get a bunch of samples to average them, which gives you higher accuracy. But it could well be that the accuracy is already plenty for whatever it is you're doing. The AD2 and AD3 have plenty of tricks up their sleeve beyong this basic usage.

Once you get it working (should be easy now), I would recommend that you put a few calls in your inner measurement loop outside of the loop. In particular, these four:

output.nodeFunctionSet(CH1, DwfAnalogOutNode.Carrier, DwfAnalogOutFunction.Square)
output.idleSet(CH1, DwfAnalogOutIdle.Initial)
output.nodeEnableSet(CH1, DwfAnalogOutNode.Carrier, True)
output.configure(CH1, True)

it's a bit cleaner to my eye to only do those once, effectively puting the analog output in a mode where the output voltage should immediately follow the output.amplitudeSet(CH1, value). It's a bit faster and cleaner to lift those calls out of the inner loop. But otherwise, a good step forward compared to your earlier version.

Last thing: while the analog input and analog output should work fine, it may be useful to verify their behavior against a properly calibrated desktop multimeter at some point. This gives you an indication of the systematic error you incur due to the calibration of the AD2/AD3 being off. It's probably not an issue, but it's always useful to do little sanity checks like that.

 

 

Hi @reddish,

I did what it has been told me but there is one discrepancy, when I am using the Waveforms application itself I can both give DC waveform and observe it from Scope. However, when I am using pydwf it cannot give waveform so I cannot observe the signal as a consequence. Here are the screen shots;

image.png.752b37acef011cea7371a4d67e821631.png

image.png.b50ae0e42a4744325a4b21f317b97178.png

image.png.49fe3a783c6bbcaf9db4a6c5ddc4d1bc.png

How is this possible? 

Best,

Öznur

 

Link to comment
Share on other sites

  • 0

I am not entirely sure what you are asking. Do you want to use pydwf and Waveforms simultaneously?

If so, it is unfortunately impossible to use the device simultaneously from two processes at the same time; the first process that claims the device effectively blocks other processes from accessing it, until it releases its claim (closes the device). So using python/pydwf and Waveforms simultaneously is not going to work.

There's a bunch of work-arounds / solutions.

- use a separate scope for monitoring/debugging (either a normal benchtop scope or a second AD2/AD3).

- re-implement your testing code in the javascript scripting engine that lives inside Waveforms, so it essentially runs as a "subroutine" inside Waveforms. I think that would could work, but I have never tried it.

 

Link to comment
Share on other sites

  • 0
On 8/25/2023 at 12:00 PM, reddish said:

I am not entirely sure what you are asking. Do you want to use pydwf and Waveforms simultaneously?

If so, it is unfortunately impossible to use the device simultaneously from two processes at the same time; the first process that claims the device effectively blocks other processes from accessing it, until it releases its claim (closes the device). So using python/pydwf and Waveforms simultaneously is not going to work.

There's a bunch of work-arounds / solutions.

- use a separate scope for monitoring/debugging (either a normal benchtop scope or a second AD2/AD3).

- re-implement your testing code in the javascript scripting engine that lives inside Waveforms, so it essentially runs as a "subroutine" inside Waveforms. I think that would could work, but I have never tried it.

 

Hi,

It is normal that you're confused. Let me explain.

I will not be using Waveforms application ever. I want the things that Waveforms do to be done by pydwf as this is the reason I chose to use pydwf.

What I am saying is, I want to use wavegen and scope at the same time ON PYDWF. So when I am giving 5V to the related pin, I want to observe that I am giving exactly the right voltage by printing them...

As we go back to my previous quote, I said I tested using wavegen and scope at the same time, it worked on Waveforms application so it shows us this is possible. However when I am trying to do it on my pydwf code, it gives the voltage but cannot read it properly. ( "Applied voltage:5.0 and Read voltage:-0.01881317031239038, Expected voltage:5.0 are NOT equal!!" )

Link to comment
Share on other sites

  • 0

Hi @Oznur Caliskan

Ok. Using analog-out and analog-in simultaneously with the same DWF device is no problem, both in Waveforms and using pydwf.

So what you're trying to do is fully possible, and there's probably a bug in your pydwf script or in the way you connect your hardware.

My advice would be to simplify your hardware (connect analog-out CH1 directly to analog-in CH1) and simplify your software (get rid of the JSON stuff), and write a simple program to have the user enter a voltage, put that on analog-out CH1, and then read that back on analog-in CH1. It can be done in about 20 lines of code I think.

Link to comment
Share on other sites

  • 0
On 9/4/2023 at 9:12 AM, reddish said:

Hi @Oznur Caliskan

Ok. Using analog-out and analog-in simultaneously with the same DWF device is no problem, both in Waveforms and using pydwf.

So what you're trying to do is fully possible, and there's probably a bug in your pydwf script or in the way you connect your hardware.

My advice would be to simplify your hardware (connect analog-out CH1 directly to analog-in CH1) and simplify your software (get rid of the JSON stuff), and write a simple program to have the user enter a voltage, put that on analog-out CH1, and then read that back on analog-in CH1. It can be done in about 20 lines of code I think.

Hi @reddish,

According to your recommendation, I simplified my code as much as possible. But the problem still continues. I think there are different issues, you can run the following code either, and connect W1,Scope1 to a pin and GNDs to GND. 

"""Demonstrate the simplest way to control the AnalogOut instrument."""
import json
import time
import math
import argparse
import keyboard
import matplotlib.pyplot as plot
from datetime import datetime
import numpy as np
from matplotlib import pyplot
from matplotlib.animation import FuncAnimation
from operator import itemgetter #listeden item çekmek için
from pydwf import DwfLibrary, DwfAnalogOutNode, DwfAnalogOutFunction, DwfAnalogOutIdle, PyDwfError, DwfAnalogInTriggerLengthCondition, DwfAnalogInTriggerType
from pydwf.utilities import openDwfDevice
from keyboard import press


def parse_test_procedure(output,input):
    CH1 = 1
    CH2 = 2

    #Applied Voltage List
    applied = 5

    print("Applied voltages:", applied)
    print("*************")

    #Expected Results List
    expected = 5

    print("Expected results:", expected)
    print("*************")

    #Tolerance Value List
    tolerance = 0.3

    print("Tolerance values:", tolerance)

    results = []

    input.reset()
    output.reset(CH1)
    x = 0

    print("Applied values", applied)
    print("Expected values", expected)
    print("Tolerance values", tolerance)

    output.nodeFunctionSet(CH1, DwfAnalogOutNode.Carrier, DwfAnalogOutFunction.Square)
    output.idleSet(CH1, DwfAnalogOutIdle.Initial)
    output.nodeEnableSet(CH1, DwfAnalogOutNode.Carrier, True)
    output.configure(CH1, True)

    voltage_DC = float(applied)
    output.amplitudeSet(CH1, voltage_DC)
    print("Given voltage:",output.amplitudeGet(CH1))
    input.status(False)
    read_voltage_CH1 = input.statusSample(CH1)
    time.sleep(0.010)
    print("Read voltage:", input.channelOffsetGet(CH1))
    expected_voltage_CH1 = expected
    if ((voltage_DC * (1 - float(tolerance))) <= read_voltage_CH1 <= (
            voltage_DC * (1 + float(tolerance)))) and (
            (expected_voltage_CH1 * (1 - float(tolerance))) <= read_voltage_CH1 <= (
            expected_voltage_CH1 * (1 + float(tolerance)))):
        x += 1
        print(
            f"\n Applied voltage:{voltage_DC} and Read voltage:{read_voltage_CH1}, Expected voltage:{expected_voltage_CH1} are equal!")
    else:
        print(
            f"Applied voltage:{voltage_DC} and Read voltage:{read_voltage_CH1}, Expected voltage:{expected_voltage_CH1} are NOT equal!!")
    if x == 3:
        print("x:", x)
        result = "TEST PASSED"
        print(result)
        results.append(result)
    else:
        print("x:", x)
        result = "TEST FAILED"
        print("\n\nAmplitude: ", voltage_DC)
        print(result)


def main():

    #parser operations must be done in main()
    parser = argparse.ArgumentParser(description="Demonstrate simple usage of the AnalogOut functionality.")
    parser.add_argument(
            "-sn", "--serial-number-filter",
            type=str,
            nargs='?',
            dest="serial_number_filter",
            help="serial number filter to select a specific Digilent Waveforms device"
        )
    args = parser.parse_args()

    try:
        dwf = DwfLibrary()
        with openDwfDevice(dwf, serial_number_filter=args.serial_number_filter) as device:
            parse_test_procedure(device.analogOut,device.analogIn)

    except PyDwfError as exception:
        print("PyDwfError:", exception)
    except KeyboardInterrupt:
        print("Keyboard interrupt, ending demo.")


if __name__ == "__main__":
    main()

You will see this:

image.png.6fcba2c6ef46023b1532f69b4bb4cbc5.png

Let me know the results, please.

Best,

Öznur

Link to comment
Share on other sites

  • 0

Hi @Oznur Caliskan

Well your program wasn't really minimal ... I condensed it a bit further (see attachment) and it works okay now, see attachment.

Cleaning it up I encountered two issue. First, I think you got tripped up with the channel numbering. The first channel is identified in the API with 0, the second channel is identified with 1.
So where you had "CH1=1; CH2=2" it should have been "CH1=0; CH2=1".

Second, there's a lot of casting-to-float going on in your program that's not really needed.

 

Anyway, hope this helps. Don't forget to attach the first channel of the AnalogOut to the first channel of the AnalogIn while testing this.

Cheers, Sidney

 

oznur.py

Link to comment
Share on other sites

  • 0
4 hours ago, reddish said:

Hi @Oznur Caliskan

Well your program wasn't really minimal ... I condensed it a bit further (see attachment) and it works okay now, see attachment.

Cleaning it up I encountered two issue. First, I think you got tripped up with the channel numbering. The first channel is identified in the API with 0, the second channel is identified with 1.
So where you had "CH1=1; CH2=2" it should have been "CH1=0; CH2=1".

Second, there's a lot of casting-to-float going on in your program that's not really needed.

 

Anyway, hope this helps. Don't forget to attach the first channel of the AnalogOut to the first channel of the AnalogIn while testing this.

Cheers, Sidney

 

oznur.py 1.91 kB · 0 downloads

Hi,

Thank you so much for your help. It worked very well. :)

Best,

Öznur

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...