Меню
+7 (495) 785-95-25
sale@lcard.ru
sale@lcard.ru
Страницы 1
1. Мы снимаем с фотоприёмного устройства сигнал биений, синусоидальные колебания высокой частоты (4кГц). Регулируя ток накачки лазера, мы можем менять их амплитуду. И когда я меняю амплитуду, в моей программе съёма сигнал по амплитуде не меняется, а в LGraph II меняется.
2. Вот архив с проектом: https://www.lcard.ru/forums/img/members … client.rar, но ужимать здесь особо нечего, тут и так только и есть съём с АЦП. Только, поскольку Microsoft не добавила в WPF контролы для построения графиков, мы используем DevExpress 23.1, так что, возможно, он у вас не соберётся.
3. И можете пояснить, почему 8000, а не 8192? разве 14-битный АЦП не возвращает код от -2^13 до 2^13-1?
Добрый день!
Карта E14-440. Библиотека lcomp, wlcomp.
После запуска съёма устройство не реагирует на изменения амплитуды входного сигнала. При этом при съёме через LGraph II этого не происходит.
Подскажите пожалуйста в чем может быть причина?
using DevExpress.XtraRichEdit.Import.Html;
using LCardClient.Infrastructure.Exceptions;
using LCardClient.Infrastructure.Services.Interfaces;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace LCardClient.Infrastructure.Services.Implementation
{
public sealed class LCardE440Controller : IAdc, IDisposable
{
private const string ApiLibrary = "wlcomp.dll";
private const string DriverBinary = "lcomp.dll";
private const string DeviceBiosName = "e440";
private const uint LAdcParam = 1;
private const uint AdcParamT3 = 2;
private const uint StreamAdc = 1;
private const int BytesPerSample = sizeof(short);
private const double CodesToVolts = 10.0 / 8192.0;
private uint driverHandle;
private uint interfaceHandle;
private double calibrationOffset;
private IntPtr dataPtr;
private IntPtr syncPtr;
private WAdcParamU0 param;
public bool CorrectionEnabled { get; private set; } = false;
public double SampleRateHz { get; private set; } = 10_000;
public IReadOnlyList<uint>? Channels { get; private set; }
public void ReadVoltages(
double seconds,
CancellationToken token,
IList<float> bufferTime,
IList<float> bufferSamples)
{
long samplesPerChannelNeeded = (long)Math.Ceiling(seconds * SampleRateHz);
long totalSamplesNeeded = samplesPerChannelNeeded * Channels.Count;
long producedSamples = 0;
double dt = 1.0 / SampleRateHz;
uint ringLength = param.IrqStep * param.Pages;
uint readIndex = 0;
short[] staging = ArrayPool<short>.Shared.Rent((int)Math.Min(ringLength, 65536u));
uint oldSyncValue = 0;
try
{
while (producedSamples < totalSamplesNeeded)
{
if (token.IsCancellationRequested)
{
return;
}
Check(GetSyncData(ref interfaceHandle, syncPtr, 0, out uint syncValue), nameof(GetSyncData));
//uint available = syncValue >= readIndex
// ? syncValue - readIndex
// : ringLength - readIndex + syncValue;
uint available = 0;
if (syncValue < oldSyncValue)
available = ringLength - oldSyncValue + syncValue;
else
available = syncValue - oldSyncValue;
oldSyncValue = syncValue;
if (available == 0)
{
Thread.SpinWait(64);
continue;
}
uint toCopy = Math.Min(available, (uint)staging.Length);
CopySamples(dataPtr, readIndex, ringLength, staging, toCopy);
var span = staging.AsSpan(0, (int)toCopy);
for (int i = 0; i < span.Length && producedSamples < totalSamplesNeeded; i++)
{
short code = span[i];
long frameIndex = producedSamples / Channels.Count;
bufferTime.Add((float)(frameIndex * dt));
bufferSamples.Add((float)((code) * CodesToVolts));
producedSamples++;
}
readIndex = (readIndex + toCopy) % ringLength;
}
}
finally
{
ArrayPool<short>.Shared.Return(staging);
}
}
public string Initialize(object? slot = null) => InitializeLCard(DriverBinary, DeviceBiosName, slot.CheckedCast<uint>());
public void Start(IReadOnlyList<uint> channels, double sampleRateHz, bool enableCorrection = false)
{
if (interfaceHandle == 0)
throw new InvalidOperationException("Device not initialized. Call Start() first.");
if (channels == null || channels.Count == 0)
throw new ArgumentException("channels empty", nameof(channels));
if (sampleRateHz <= 0)
throw new ArgumentOutOfRangeException(nameof(sampleRateHz));
param = BuildAdcParameters(channels, sampleRateHz);
uint ringSize = param.IrqStep * param.Pages;
Check(RequestBufferStream(ref interfaceHandle, ref ringSize, StreamAdc), nameof(RequestBufferStream));
Check(FillDAQparameters(ref interfaceHandle, ref param, AdcParamT3), nameof(FillDAQparameters));
Check(SetParametersStream(
ref interfaceHandle,
ref param,
AdcParamT3,
out uint _,
out dataPtr,
out syncPtr,
StreamAdc), nameof(SetParametersStream));
if (dataPtr == nint.Zero || syncPtr == nint.Zero)
throw new InvalidOperationException("Driver returned invalid data or sync pointers.");
Channels = channels;
SampleRateHz = sampleRateHz;
CorrectionEnabled = enableCorrection;
Check(EnableCorrection(ref interfaceHandle, (ushort)(CorrectionEnabled ? 1 : 0)), nameof(EnableCorrection));
Check(InitStartLDevice(ref interfaceHandle), nameof(InitStartLDevice));
Check(StartLDevice(ref interfaceHandle), nameof(StartLDevice));
}
public void Stop()
{
Check(StopLDevice(ref interfaceHandle), nameof(StopLDevice));
}
public double ReadOneSample(uint channel)
{
if (interfaceHandle == 0)
throw new InvalidOperationException("Device not initialized.");
var asyncParam = new AsyncParam(128)
{
s_Type = 6,
NCh = 1u,
Chn = { [0] = channel }
};
asyncParam.Chn[0] = channel;
CheckError(IoAsync(ref interfaceHandle, ref asyncParam), "IoAsync failed when reading a sample.");
short value = unchecked((short)asyncParam.Data[0]);
return value;
}
public static uint MakeChWord(int channel, bool differential, int gainCode)
{
uint word = (uint)(gainCode & 0x3) << 6;
if (differential)
{
word |= (uint)(channel & 0x0F);
}
else
{
word |= 1u << 5 | (uint)(channel & 0x1F);
}
return word;
}
public void Dispose()
{
CloseDevice();
GC.SuppressFinalize(this);
}
~LCardE440Controller() => CloseDevice();
private static WAdcParamU0 BuildAdcParameters(IReadOnlyList<uint> channels, double sampleRateHz)
{
var param = new WAdcParamU0(true)
{
s_Type = LAdcParam,
FIFO = 4096,
IrqStep = 4096,
Pages = 32,
AutoInit = 1,
dRate = sampleRateHz * channels.Count / 1000.0,
dKadr = 0,
dScale = 0,
SynchroType = 0,
SynchroSensitivity = 0,
SynchroMode = 0,
AdChannel = 0,
AdPorog = 0,
NCh = (uint)channels.Count,
IrqEna = 1,
AdcEna = 1
};
for (int i = 0; i < channels.Count; i++)
{
param.Chn[i] = channels[i];
}
return param;
}
private string InitializeLCard(string driverBinary, string bios, uint? slot)
{
CloseDevice();
driverHandle = LoadAPIDLL(driverBinary);
if (driverHandle == 0)
throw new InvalidOperationException("LoadAPIDLL failed.");
uint slotToUse = slot ?? DetectE440Slot(driverBinary, bios);
uint err = 0;
interfaceHandle = CallCreateInstance(ref driverHandle, slotToUse, ref err);
if (err != 0 || interfaceHandle == 0)
throw new InvalidOperationException($"CallCreateInstance failed for slot {slotToUse} (err={err}).");
uint openStatus = OpenLDevice(ref interfaceHandle);
if (openStatus == 0xFFFFFFFFu)
throw new InvalidOperationException($"OpenLDevice failed for slot {slotToUse} (status=0x{openStatus:X}).");
var slotInfo = new SlotPar();
uint slotStatus = GetSlotParam(ref interfaceHandle, ref slotInfo);
if (slotStatus != 0)
throw new InvalidOperationException($"GetSlotParam failed for slot {slotToUse} (status=0x{slotStatus:X}).");
CheckError(LoadBios(ref interfaceHandle, bios), $"LoadBios failed. Ensure {bios}.bin is present.");
CheckError(PlataTest(ref interfaceHandle), "PlataTest failed.");
var descr = new PlataDescr(0);
CheckError(ReadPlataDescr(ref interfaceHandle, ref descr), "ReadPlataDescr failed.");
calibrationOffset = ReadOneSample(48u);
return $"Serial number: {Encoding.ASCII.GetString(descr.SerNum)}, " +
$"board name: {Encoding.ASCII.GetString(descr.BrdName)}, " +
$"DSP type: {Encoding.ASCII.GetString(descr.DspType)}";
}
private void CloseDevice()
{
if (interfaceHandle != 0)
{
CloseLDevice(ref interfaceHandle);
interfaceHandle = 0;
}
if (driverHandle != 0)
{
FreeAPIDLL(ref driverHandle);
driverHandle = 0;
}
}
private uint DetectE440Slot(string driverBinary, string bios)
{
var inspected = new StringBuilder();
for (uint slot = 0; slot < 32; slot++)
{
uint err = 0;
uint testHandle = CallCreateInstance(ref driverHandle, slot, ref err);
if (err != 0 || testHandle == 0)
{
inspected.AppendLine($"slot {slot}: CallCreateInstance err={err}");
continue;
}
bool opened = false;
try
{
uint openStatus = OpenLDevice(ref testHandle);
if (openStatus == 0xFFFFFFFFu)
{
inspected.AppendLine($"slot {slot}: OpenLDevice status=0x{openStatus:X}");
continue;
}
opened = true;
var slotInfo = new SlotPar();
uint slotStatus = GetSlotParam(ref testHandle, ref slotInfo);
inspected.AppendLine($"slot {slot}: boardType={slotInfo.BoardType} status=0x{slotStatus:X}");
if (slotStatus != 0)
continue;
if (slotInfo.BoardType == SlotPar.BoardTypeE440)
return slot;
if (slotInfo.BoardType == 0)
{
if (LoadBios(ref testHandle, bios) != 0)
continue;
if (PlataTest(ref testHandle) != 0)
continue;
var descr = new PlataDescr(0);
if (ReadPlataDescr(ref testHandle, ref descr) != 0)
continue;
string name = BytesToAscii(descr.BrdName);
inspected.AppendLine($"slot {slot}: name={name}");
if (name.Contains("E-14-440", StringComparison.OrdinalIgnoreCase) ||
name.Contains("E440", StringComparison.OrdinalIgnoreCase))
return slot;
}
}
finally
{
if (opened && testHandle != 0)
CloseLDevice(ref testHandle);
}
}
throw new InvalidOperationException($"E14-440 board not found. Specify slot explicitly.\nChecked:\n{inspected}");
}
private static string BytesToAscii(byte[] bytes)
{
int len = Array.IndexOf(bytes, (byte)0);
if (len < 0)
len = bytes.Length;
return Encoding.ASCII.GetString(bytes, 0, len);
}
private void CopySamples(nint dataPtr, uint readIndex, uint ringLength, short[] staging, uint samplesToCopy)
{
if (samplesToCopy == 0)
return;
uint firstChunk = Math.Min(samplesToCopy, ringLength - readIndex);
uint secondChunk = samplesToCopy - firstChunk;
GCHandle destHandle = GCHandle.Alloc(staging, GCHandleType.Pinned);
try
{
nint destPtr = destHandle.AddrOfPinnedObject();
if (firstChunk > 0)
{
nint srcPtr = nint.Add(dataPtr, (int)(readIndex * BytesPerSample));
Check(GetDataFromBuffer(ref interfaceHandle, srcPtr, destPtr, firstChunk * BytesPerSample, 0), nameof(GetDataFromBuffer));
}
if (secondChunk > 0)
{
nint srcPtr = dataPtr;
nint destPtr2 = nint.Add(destPtr, (int)(firstChunk * BytesPerSample));
Check(GetDataFromBuffer(ref interfaceHandle, srcPtr, destPtr2, secondChunk * BytesPerSample, 0), nameof(GetDataFromBuffer));
}
}
finally
{
destHandle.Free();
}
}
private static void Check(uint status, string where)
{
if (status != 0)
throw new InvalidOperationException($"{where} failed (status 0x{status:X}).");
}
private static void CheckError(uint status, string message)
{
if (status != 0)
throw new InvalidOperationException(message);
}
#region Native structures
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct AsyncParam
{
public uint s_Type;
public uint FIFO;
public uint IrqStep;
public uint Pages;
public double dRate;
public uint Rate;
public uint NCh;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public uint[] Chn;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public uint[] Data;
public uint Mode;
public AsyncParam(int channelCount = 128)
{
s_Type = 0;
FIFO = 0;
IrqStep = 0;
Pages = 0;
dRate = 0;
Rate = 0;
NCh = 0;
Mode = 0;
Chn = new uint[channelCount];
Data = new uint[channelCount];
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PlataDescr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public byte[] SerNum;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public byte[] BrdName;
public byte Rev;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public byte[] DspType;
public int Quartz;
public ushort IsDacPresent;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
public ushort[] Reserv1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public ushort[] KoefADC;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public ushort[] KoefDAC;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public ushort[] Custom;
public PlataDescr(int _)
{
SerNum = new byte[9];
BrdName = new byte[5];
DspType = new byte[5];
Reserv1 = new ushort[7];
KoefADC = new ushort[8];
KoefDAC = new ushort[4];
Custom = new ushort[32];
Rev = 0;
Quartz = 0;
IsDacPresent = 0;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct WAdcParamU0
{
public uint s_Type;
public uint FIFO;
public uint IrqStep;
public uint Pages;
public uint AutoInit;
public double dRate;
public double dKadr;
public double dScale;
public uint Rate;
public uint Kadr;
public uint Scale;
public uint FPDelay;
public uint SynchroType;
public uint SynchroSensitivity;
public uint SynchroMode;
public uint AdChannel;
public uint AdPorog;
public uint NCh;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public uint[] Chn;
public uint IrqEna;
public uint AdcEna;
public WAdcParamU0(bool _)
{
s_Type = 0;
FIFO = 4096;
IrqStep = 4096;
Pages = 4;
AutoInit = 1;
dRate = 0;
dKadr = 0;
dScale = 0;
Rate = 0;
Kadr = 0;
Scale = 0;
FPDelay = 0;
SynchroType = 0;
SynchroSensitivity = 0;
SynchroMode = 0;
AdChannel = 0;
AdPorog = 0;
NCh = 0;
Chn = new uint[128];
IrqEna = 1;
AdcEna = 1;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SlotPar
{
public uint Base;
public uint BaseL;
public uint Base1;
public uint BaseL1;
public uint Mem;
public uint MemL;
public uint Mem1;
public uint MemL1;
public uint Irq;
public uint BoardType;
public uint DSPType;
public uint Dma;
public uint DmaDac;
public uint DtaReg;
public uint IdmaReg;
public uint CmdReg;
public uint IrqReset;
public uint DtaArray;
public uint ReadyReg;
public uint CfgReg;
public const uint BoardTypeE440 = 30u;
}
#endregion
#region Native bindings
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint LoadAPIDLL(string dll);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint FreeAPIDLL(ref uint hDll);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint CallCreateInstance(ref uint hDll, uint slot, ref uint err);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint OpenLDevice(ref uint hIfc);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint CloseLDevice(ref uint hIfc);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint LoadBios(ref uint hIfc, string bios);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint PlataTest(ref uint hIfc);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint IoAsync(ref uint hIfc, ref AsyncParam p);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint ReadPlataDescr(ref uint hIfc, ref PlataDescr p);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint GetSlotParam(ref uint hIfc, ref SlotPar slot);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint FillDAQparameters(ref uint hIfc, ref WAdcParamU0 p, uint spType);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint RequestBufferStream(ref uint hIfc, ref uint size, uint streamId);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint SetParametersStream(
ref uint hIfc,
ref WAdcParamU0 sp,
uint spType,
out uint usedSize,
out nint data,
out nint sync,
uint streamId);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint EnableCorrection(ref uint hIfc, ushort ena);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint InitStartLDevice(ref uint hIfc);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint StartLDevice(ref uint hIfc);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint StopLDevice(ref uint hIfc);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint GetSyncData(ref uint hIfc, nint syncPtr, uint offset, out uint syncValue);
[DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern uint GetDataFromBuffer(ref uint hIfc, nint dataPtr, nint destination, uint size, uint mask);
#endregion
}
}P.S. Если будут вопросы к импортам функций, ULONG в Win32 - это 32 бита, в C# этому соответствует uint.
Страницы 1
Адрес: 117105, Москва, Варшавское шоссе, д. 5, корп. 4
Многоканальный телефон:+7 (495) 785-95-25
Письма и запросы: lcard@lcard.ru
Отдел продаж: sale@lcard.ru
Техническая поддержка: support@lcard.ru
Время работы: с 9-00 до 19-00 мск