Наличие задержки по времени на входе LiveChart - PullRequest
0 голосов
/ 05 апреля 2020

Я хочу спросить - подходит ли LiveChart для осциллографа на C# WPF?

Поскольку в настоящее время я сталкиваюсь с некоторой проблемой, связанной с задержкой ввода. Задержка входного сигнала (справа от формы сигнала 32.44) не синхронизируется с другим графиком, когда я открываю более двух графиков.

Может понадобиться помощь ваших парней и дать представление об этом.

Я не уверен, что это из-за ограничений библиотеки LiveChart или, возможно, из-за другой проблемы.

Вы можете обратиться к изображению ниже

Задержка ввода на 32.44

enter image description here

Вы можете посмотреть мой код для реализации диаграммы ниже.

    public MainWindow()
    {
        InitializeComponent();

        var mapper = Mappers.Xy<MeasureModel>()
            .X(model => model.DateTime.Ticks)   //use DateTime.Ticks as X
            .Y(model => model.Value);           //use the value property as Y

        //lets save the mapper globally.
        Charting.For<MeasureModel>(mapper);

        //the values property will store our values array
        ChartValues1 = new ChartValues<MeasureModel>();
        ChartValues2 = new ChartValues<MeasureModel>();
        ChartValues3 = new ChartValues<MeasureModel>();
        ChartValues4 = new ChartValues<MeasureModel>();

        //lets set how to display the X Labels
        DateTimeFormatter = value => new DateTime((long)value).ToString("mm:ss");

        //AxisStep forces the distance between each separator in the X axis
        V.AxisStep = TimeSpan.FromSeconds(1).Ticks;

        //AxisUnit forces lets the axis know that we are plotting seconds
        //this is not always necessary, but it can prevent wrong labeling
        V.AxisUnit = TimeSpan.TicksPerSecond;

        A.SetAxisLimits(DateTime.Now);

        A.IsReading1 = false;
        B.IsReading2 = false;
        C.IsReading3 = false;
        D.IsReading4 = false;

        DataContext = this;

        Screen1.UpdaterState = UpdaterState.Paused;
        Screen2.UpdaterState = UpdaterState.Paused;
        Screen3.UpdaterState = UpdaterState.Paused;
        Screen4.UpdaterState = UpdaterState.Paused;
        initializeComboBox();

    }


    private void Read1()
    {
        while (A.IsReading1)
        {
            Thread.Sleep(100);
            var now = DateTime.Now;

            ChartValues1.Add(new MeasureModel
            {
                DateTime = now,
                Value = A.result1
            });

            A.SetAxisLimits(now);

            //lets only use the last 150 values
            if (ChartValues1.Count > 150) ChartValues1.RemoveAt(0);
        }
    }


    private void Read2()
    {
        while (B.IsReading2)
        {
            Thread.Sleep(100);
            var now = DateTime.Now;

            ChartValues2.Add(new MeasureModel
            {
                DateTime = now,
                Value = B.result2
            });

            B.SetAxisLimits(now);

            //lets only use the last 150 values
            if (ChartValues2.Count > 150) ChartValues2.RemoveAt(0);
        }
    }

    private void StartScope()
    {
        if (A.IsReading1 && A.screen1)
        {
            A.result1 = 0;
            Task.Factory.StartNew(Read1);
            Screen1.UpdaterState = UpdaterState.Running;
            A.screen1 = false;
        }

        else if (B.IsReading2 && B.screen2)
        {
            B.result2 = 0;
            Task.Factory.StartNew(Read2);
            Screen2.UpdaterState = UpdaterState.Running;
            B.screen2 = false;
        }
    }


public class Screen1 // Separate Class for Chart 1
{
    public bool IsReading1 { get; set; }
    public int result1 { get; set; }
    public bool screen1 { get; set; }

    public double _axisMax;
    public double _axisMin;

    public double AxisMax
    {
        get { return _axisMax; }
        set
        {
            _axisMax = value;
            OnPropertyChanged("AxisMax");
        }
    }
    public double AxisMin
    {
        get { return _axisMin; }
        set
        {
            _axisMin = value;
            OnPropertyChanged("AxisMin");
        }
    }

    public void SetAxisLimits(DateTime now)
    {
        AxisMax = now.Ticks + TimeSpan.FromSeconds(1).Ticks; // lets force the axis to be 1 second ahead
        AxisMin = now.Ticks - TimeSpan.FromSeconds(8).Ticks; // and 8 seconds behind
    }

    #region INotifyPropertyChanged implementation

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName = null)
    {
        if (PropertyChanged != null)
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion

}


public class Screen2 // Separate Class for Chart 2
{

    public bool IsReading2 { get; set; }
    public int result2 { get; set; }
    public bool screen2 { get; set; }

    public double _axisMax;
    public double _axisMin;
    public double AxisMax
    {
        get { return _axisMax; }
        set
        {
            _axisMax = value;
            OnPropertyChanged("AxisMax");
        }
    }
    public double AxisMin
    {
        get { return _axisMin; }
        set
        {
            _axisMin = value;
            OnPropertyChanged("AxisMin");
        }
    }

    public void SetAxisLimits(DateTime now)
    {
        AxisMax = now.Ticks + TimeSpan.FromSeconds(1).Ticks; // lets force the axis to be 1 second ahead
        AxisMin = now.Ticks - TimeSpan.FromSeconds(8).Ticks; // and 8 seconds behind
    }

    #region INotifyPropertyChanged implementation

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName = null)
    {
        if (PropertyChanged != null)
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion

}

1 Ответ

0 голосов
/ 07 апреля 2020

Горизонтальное смещение, которое вы наблюдаете, относится к единице оси X. Ваши значения DateTime не округляются, например, до секунд. Вы всегда берете полную метку времени, включая миллисекунды. При выделении секунд временные метки разных графиков совпадают. Но, глядя на миллисекунды, нет. Это совершенно очевидно при сравнении желтого ряда с синим: временные метки желтого ряда попадают прямо между голубыми рядами значений x, создавая «смещение», что абсолютно правильно, но не желательно. Замена единицы по оси x на DateTime с фиксированным интервалом времени прохождения (например, 5, 10, 15) исправит это.

Текущая реализация не имитирует c, как работает осциллограф. Вы не показываете отметки времени на оси X или абсолютные значения на оси Y. Вы просто предоставляете постоянную сетку или шкалу для измерения сигнала. Вес интервала сетки (обе оси) настраивается пользователем. Например, каждый горизонтальный интервал составляет 5 мс, а вертикальный интервал - 1 Вольт. Таким образом, вам не нужна временная метка (DateTime) для рисования сигнала, но время прохождения (TimeSpan). Вы должны эмулировать триггер, чтобы выбрать правильный диапазон значений, чтобы обеспечить стабильный снимок сигнала. Таким образом, количество значений для построения экрана зависит от текущей временной базы. Число не является фиксированным, например, последние 150 значений. Вы можете использовать время прохождения или рассчитанную частоту, чтобы масштабировать сигнал, чтобы он соответствовал текущему выбранному временному интервалу сетки видового экрана. Допустим, ваш видовой экран имеет сетку 10 * 8, и пользователь устанавливает временную базу на 5 мс, затем вы должны построить 50 мс сигнала и обозначить ось х 5 мс, 10 мс, 15 мс, ....

Ваш стиль программирования не является объектно-ориентированным. Вы должны либо использовать наследование для инкапсуляции общего кода внутри базового класса. Таким образом, вместо двух классов Screen1 и Screen2 у вас должен быть один класс Screen, который имеет все общие логики c, а затем извлекает Screen1 и Screen2 из него и добавляет некоторую спецификацию c функции. Или, если они не имеют определенных c функций, то есть один класс Screen. Затем вы создаете два или n экземпляров этого класса

var screen1 = new Screen();
var screen2 = new Screen();

Всегда избегайте дублирования кода. То же относится и к Read1 и Read2. Оба содержат одинаковый код. Вы должны объединить оба метода, а также читать каналы параллельно:

// Read each channel in a separate thread
private void ReadChannels()
{
  Task.Run(() => ReadChannel(this.ChartValues1, this.ScreenA));
  Task.Run(() => ReadChannel(this.ChartValues2, this.ScreenB));
  Task.Run(() => ReadChannel(this.ChartValues3, this.ScreenC));
}

private void ReadChannel(ChartValues<MeasureModel> chartValues, Screen screen)
{
  while (screen.IsReading1)
  {
    Thread.Sleep(100);
    var now = DateTime.Now;

    chartValues.Add(new MeasureModel
    {
      DateTime = now,
      Value = screen.result1
    });

    screen.SetAxisLimits(now);

    //lets only use the last 150 values
    if (chartValues.Count > 150) 
      chartValues.RemoveAt(0);
  }
}

Я рекомендую взглянуть на LiveCharts Geared , так как он может похвастаться высокой производительностью в сценарии живых графиков ios.
Также у живых графиков есть некоторые ограничения. Вы не можете изменить смещение сюжета. Это базовое требование c для осциллографа, чтобы свободно выровнять сигнал с сеткой, чтобы визуально интерпретировать сигнал. Вы должны были бы реализовать это самостоятельно. Вы можете нарисовать несколько рядов на одном графике, но не можете их сложить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...