Triggering an output task with NIDAQmx - python

I'm having trouble carrying out what I think should be a pretty straightforward task on a NIDAQ usb6002: I have a low frequency sine wave that I'm measuring at an analog input channel, and when it crosses zero I would like to light an LED for 1 second. I'm trying to use the nidaqmx Python API, but haven't been able to clear up some of my basic questions with the documentation. https://nidaqmx-python.readthedocs.io/en/latest/
If anyone can offer any thoughts about the code or the basic logic of my setup, that would be hugely helpful.
Here's what I have tried so far. I start with some imports and the definition of my channels:
import matplotlib.pyplot as plt
from math import *
import nidaqmx
from nidaqmx import *
from nidaqmx.constants import *
import time
V_PIN = "Dev1/ai6"
LED_PIN = "Dev1/ao0"
I understand how tasks and things work generally- I can read and plot a signal of a given sampling rate and number of samples using task.ai_channels methods without any trouble. But here's my best guess at how to carry out "detect zero and trigger output":
writeLED = nidaqmx.Task('LED')
writeLED.ao_channels.add_ao_voltage_chan(LED_PIN)
writeLED.timing.cfg_samp_clk_timing(1)
writeLED.triggers.start_trigger.cfg_anlg_edge_start_trig(V_PIN,trigger_level = 0)
writeLED.write([5], auto_start=True)
This gives me the error below at the cfg_anlg_edge line
DaqError: Requested value is not a supported value for this property. The property value may be invalid because it conflicts with another property.
Property: DAQmx_StartTrig_Type
Requested Value: DAQmx_Val_AnlgEdge
Possible Values: DAQmx_Val_DigEdge, DAQmx_Val_None
I don't know why an analog input channel wouldn't be supported here. Page 245 of this document makes it sound like it should be: https://media.readthedocs.org/pdf/nidaqmx-python/latest/nidaqmx-python.pdf
I'm sure there are other problems with the code, too. For example, it seems like the sample clock manipulations are quite a bit more complicated than what I've written above, but I haven't been able to find anything that explains how it would work in this situation.
Thanks in advance for any help!

With NI, it's "RTFMs"
When programming NI devices, you usually need two manuals.
NI-DAQmx Help (for the programming part)
the device specification (for the device part)
You need both because the NI-DAQmx API supports every DAQ device NI makes, but not every device has the same capabilities. "Capabilities" includes more than how many channels of each kind, but also the timing and triggering subsystems as well as internal signal routing. A DAQmx application that runs with one device is not guaranteed to run with another because the application might use the API in a way the second device cannot support.
Finally, on the documentation front, any given NI DAQ device typically belongs to family of related devices and these families also have a manual called User Guide. These User Guides act as a bridge between the API and device spec, helping you understand how the device responds to commands. For the 6002, the family is "Low-Cost DAQ USB Device".
Analog trigger for analog output on NI 6002
Your determination is correct that
writeLED.triggers.start_trigger.cfg_anlg_edge_start_trig(V_PIN,trigger_level = 0)
is possible, just not for the USB 6002. This line is asking the analog output subsystem to use an analog edge trigger, but the analog output subsystem for the 6002 only has these trigger capabilities:
software
PFI 0
PFI 1
For this device, you're only option is the software trigger because the PFI lines are digital triggers and their trigger level is specified to be between 0.8 V and 2.3 V.
Change your Python program to detect a zero-crossing from the analog input stream and, when it does, make it call stop() and then start() on the AO task.
The reason for the stop-start sequence is retriggering: you want to light the LED for each zero crossing, but a task cannot be restarted unless it has either been stopped (by the API or by completing its task) or configured for retriggering. Because the 6002 is in the low-cost family, this hardware feature isn't available, so you must use the API to stop the AO task or wait for the AO generation to complete before restarting the pulse for the LED
6002 AO Specification

Software triggering is not real-time, you will have non-deterministic delay before the led turns on. This depends on your program, interfaces, usb latencies, pc performances...
Otherwise, you can use a comparator (like lm393) to trigger a digital input (PFI0 or PFI1).
Though it's just an LED, it is probably not critical if the delay varies within milliseconds.

Related

Using Python to playback mic input to PC in real-time

I'm trying to use Python to 'mic-monitor', i.e., to playback the mic signal through the headphones in real-time, in addition to any other output signal from the PC.
I can accomplish this by amending my PC's playback settings, but I want to do it with Python, so that I can program a Raspberry Pi to mic-monitor my cheap headset for the PS4.
I can almost accomplish this with PyAudio or sounddevice, but there is a small but significant delay. So:
Is there a way to eradicate that latency with Python, for example by somehow accessing my PC's inputs more directly?
Otherwise, why is it not possible with Python?
Can I somehow configure the Raspberry Pi to mic monitor in the same way as my PC?
Sounddevice code is shown below for reference:
import sounddevice as sd
duration = 5.5 # seconds
def callback(indata, outdata, frames, time, status):
if status:
print(status)
outdata[:] = indata
with sd.Stream(channels=2, callback=callback):
sd.sleep(int(duration * 1000))
There will always be latency with a computer in-between. Professional audio gear is usually custom built for minimal latency (or it's just analog). To reduce latency you need to record smaller chunks at a time before sending them to the output which does introduce more processing overhead. Using smaller chunks can also at some point introduce more jitter in the signal because the inter-frame latency might not keep up with the sample rate. PortAudio is probably likely able to be configured to have a bit less latency, but you're probably getting a lot of the delay from the OS and audio drivers as well. Here's a page discussing how you can optimize the OS and audio drivers for minimal latency on a Raspberry Pi. PortAudio (the driver behind most python audio libraries), also has a discussion on audio driver latency based on your operating system.
looking at the documentation for sd.Stream, it looks like even if you specify a smaller blocksize, due to the implementation, it may make latency even worse.
There is however an option to specify an exact latency (if a particular delay is desirable) or to achieve a best effort "as fast as possible" by specifying latency = "low" This attempts to take into account the specific hardware you're using, and go as fast as possible.

Listening to i2c input changes with python

Let's say I have a raspberry pi here and I want to write a Python script that turns on the light as soon as a i2c signal reaches the pi and some pin gets high. I do not want to use polling for this task, as it could slow down the process
(I know just a bit but I'ts just bad practice and puts load on the CPU and so on, so I don't want to permacycle asking for the input state)
Is there any kind of server or callback function I could use to realize this with a python script? Which library could I use to reach such a behaviour?
First Ideas are enviromental variables/the i2c Interface in the Linux system that I could listen to constantly somehow and catch it to make it do what I want it to.
As I see it no need to use python but I don't see whole picture so I don't know if this will help You,
just regarding this part of question:
Is there any kind of server or callback function...
rpio.poll(pin, callback());
Watch pin for changes and execute the callback callback() on events.
callback() takes a single argument, the pin which triggered the callback.
The optional direction argument can be used to watch for specific
events:
rpio.POLL_LOW: poll for falling edge transitions to low.
rpio.POLL_HIGH: poll for rising edge transitions to high.
rpio.POLL_BOTH: poll for both transitions (the default).
Complete documentation - this is the npm module documentation
My example of configuring node.js server

Sorting Input from Multiple HIDs in Windows

I have found a number of answers in pulling information from HIDs in Linux, but not many in Windows.
I have created a system where a person can scan an ID badge when entering a briefing that logs their attendance into a database. It utilizes a Python 3.4 front end which queries and then updates a MongoDB database.
Currently, I have a USB Barcode Scanner which, when scanning, acts as a keyboard and "types" what the barcode says, followed by a CR. I also have a window which takes the text input and then closes the window and executes a database query and update when the CR is received.
The current issue is speed. I have been asked to expand the system so that one computer with a USB hub can take 4-8 of these Barcode Scanners at the same time, attempting to increase scanning rate to 1000 people every 5 minutes.
What I am afraid will happen is that if two scans happen at almost the same time, then their inputs will overlap, generating an invalid query and resulting in both individuals not being logged.
As far as I can understand, I need to place each Scanner in its own thread to prevent overlapping data, and I do not want to "lock" input from the other scanners when the system detects a scan beginning as this system is all about speed. However, I am unsure of how to differentiate the devices and how to implement the system.
Any solutions would be appreciated! Please take note that I am unfamiliar with HID use in this sense, and only have a basic background in multi-threading.
In this scenario I will suggest using scanners/readers that can emulate serial (COM) port. As HID device writes to same bus then there is a huge probability that output from two or more devices could by mixed-up.
More over I will add a device id string to a prefix like dev01. Binding to a com port can be used by pySerial module.
Any comments welcome!

Serial communication over USB converter in Python - how to aproach this?

A python program needs to accept a string every second from a serial port. I plan on using a RS-232 to USB converter. The application is going to work under Ubuntu 10.04.
How do I approach this? Do I use pySerial or libusb?
There needs to be done some processing in the meantime, so synchronous communication is not viable. Do I use some kind of interrupts or do I need to open separate threads? Or do I use blocking reads, believing that 1s is enough for my computations (it is plenty ... for now)?
I know, RTFM, but heading in the correct direction from the start will save me a lot of time! Thanks for bearing with me.
If your RS232-USB converter has a driver in Ubuntu that makes it look like a COM port then you will want to use pySerial (the interface is the same as any other COM port). If there is no driver for your device then you might have to use libusb and find the protocol for your specific device. Most major RS232-USB converters these days have usbserial based drivers committed and maintained in the Linux kernel. Simply check with your vendor for this.
There are many ways to do parallel processing but typically I write my applications in two ways:
Have a read thread that does nothing but read and fill a local thread safe buffer so data is ready for other threads when needed.
Have a read thread that reads data, determines where it goes and delivers it via messaging/event processing to the component that needs it.
The decisions here will depend on what your goals are, and how much processing is needed outside the reading.

Hardware interrupt for synchronous data acquisition

I am looking for a simple means of triggering my data acquisition software using an external TTL pulse. I need to sample data from multiple sources synchronously with a 5 Hz reference clock. The acquisition does not need real-time priority but I want to ensure that my software is triggered as soon as possible and exactly once per external clock cycle. I would prefer to do this by somehow getting an interrupt from the external trigger without needing to use a fast polling loop. As far as I can tell, you can't just use a parallel port pin for an interrupt in a modern OS like Linux. Any ideas?
I am also thinking of generating broadcast packets on my network to notify other machines on the network that a trigger event has occurred. Due to network latency however there may not be enough time available in the 200ms period between triggers to do the acquisition.
Rather than use the parallel port, have you considered using a serial device? Since you have a TTL signal, you'll possibly need a level converter to convert TTL to RS232 +/- 12V levels. Once you're using a serial device, you can use the standard serial ioctl() calls to detect a change in control signal status.
Specifically, you could use the TIOCMIWAIT ioctl on the connected serial device to wait for a change on say the DCD line, which you would connect to your clock source.
Your user space application would be blocked waiting in the TIOCMIWAIT ioctl system call until there is a change of status on your clock line, at which point your app would become runnable and return from the ioctl. You might have to take care to ensure that you handle the case where you get a change of status interrupt on both rising and falling edges of your serial control signals. On some UART hardware (eg TL16C554A UART) it's possible that you'll only get an interrupt for a signal transitioning in a single direction. For the TL16C554A for example, the TIOCMIWAIT would only fall through on the rising edge of any Ring Indicate signal change.
Using the serial ioctls in this manner also has the advantage that you could use a USB-Serial dongle that supports TIOCMIWAIT if required (eg PL2303), and still retain user level software compatibility, albeit at the expense of increased latency due to USB.
If you require lower latency than can be achieved through user space, you'd be best to write a kernel driver module which can handle the timing and sampling, but I wouldn't suggest this route unless absolutely needed. It's easier to develop user space code.
Here's some incomplete sample C code snippets for using the TIOCMIWAIT ioctl.
int serial_fd = open(cmdline.device_name, O_RDWR | O_NONBLOCK | O_NOCTTY);
static const unsigned int ri_flag = TIOCM_RNG;
/* Set up serial port here using tcsetattr. Set CRTSCTS | CLOCAL to ensure status interrupts
* are generated.
*/
while (1) {
/* Wait for positive RI transition. TIOCMIWAIT takes a mask
* as argument, only returning when the appropriate signal has changed.
*/
if (ioctl(serial_fd, TIOCMIWAIT, ri_flag)) {
fprintf(stderr, "ioctl() failed waiting for RI edge [%s]\n", strerror(errno));
break;
}
/* Do sensor sampling here. You could use TIOCMGET to first verify that
* the clock line is in the expected state, eg high, before continuing.
*/
}
Polling is a fine method for such a slow data rate. Poll at 1 ms. That should be fine. Trying to use a hardware interrupt is going to cause much pain.
Google for "Interrupt Linux GPIO" if you want to do it the hard way. :)
https://developer.ridgerun.com/wiki/index.php/How_to_use_GPIO_signals
Consider connecting the external pulse source to the 'CD' ping of a real (! not a USB to RS232 converter) serial port. Then you can use the "PPS api" to get an as exact timestamp from which the pin "went high" as possible.
You might need a TTL signal shifter; not all serial ports work correctly with TTL signal levels.
The PPS api is normally used for time keeping. E.g. connect the PPS pin of a GPS module to your pc and let NTP sync with that. This gives you microsecond accuracy.
This PPS api is supposed to be more accurate than any other userspace solution (e.g. the TIOCMIWAIT ioctl) as it is completely handled in the kernel, immediately when the interrupt (as triggered by the CD signal change) comes in. With the ioctl solution you have at least a context switch. I did some testing on a raspberry pi and a userspace solution gives at least 6us jitter.
The PPS api gives you a timestamp from when the pulse was detected.
Why not use a USB to RS232 converter: I read somewhere (in relation to timekeeping) that USB devices are polled once every +/- 1ms. This polling frequency also depends on how busy the system is with other USB devices and I think the internal clock of the USB device may also influence things. I've never measured this though.
Relevant URLS:
https://www.rfc-editor.org/rfc/rfc2783 RFC describing the PPS API
http://linuxpps.org/ Linux implementation the PPS API (it is in the standard kernel)
http://comments.gmane.org/gmane.comp.hardware.gps.gpsd.user/3747 a thread which mentions why not to use USB for timekeeping
Regarding the network functionality: use UDP broadcasts, do not use TCP.
I ended up using the serial port CTS line for the trigger using the TIOCMIWAIT ioctl per Austin Phillips answer. Since RS232 requires +/-12V levels I was able to get the necessary power for this level shifter from the other serial control lines.
The Python code to implement this solution can be found in question: Python monitor serial port (RS-232) handshake signals

Categories

Resources