Российский производитель и разработчик сертифицированного измерительного оборудования с 1987 года


E14-440. Неправильный съём данных (C#)

Вы не вошли.

 Поиск | Регистрация | Вход 

13.11.2025 18:06:27
#1

Участник
Здесь с 13.11.2025
Сообщений: 2

E14-440. Неправильный съём данных (C#)

Добрый день!

Карта 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.

13.11.2025 19:00:02
#2

Сотрудник "Л Кард"
Здесь с 24.04.2014
Сообщений: 1,524

Re: E14-440. Неправильный съём данных (C#)

1. Я так понимаю, что данный экземпляр модуля E14-440 вполне нормально живёт из-под "LGraph II", а в Вашей программе на C# есть проблемы.  Вот только не совсем понятно, что именно подразумевается под фразой "не реагирует на изменения амплитуды входного сигнала"?
2. Присылайте Ваш вариант проекта программы. Только желательно ужать её до сути проблемы.
2. Строчку

private const double CodesToVolts = 10.0 / 8192.0;

надо бы заменить на

private const double CodesToVolts = 10.0 / 8000.0;
Сегодня 09:45:46
#3

Участник
Здесь с 13.11.2025
Сообщений: 2

Re: E14-440. Неправильный съём данных (C#)

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?

Контакты

Адрес: 117105, Москва, Варшавское шоссе, д. 5, корп. 4

Многоканальный телефон:+7 (495) 785-95-25

Письма и запросы: lcard@lcard.ru
Отдел продаж: sale@lcard.ru
Техническая поддержка: support@lcard.ru

Время работы: с 9-00 до 19-00 мск