Дополнительные функции драйвера FTDI | others | adminstuff

Дополнительные функции драйвера FTDI | others | adminstuff ОБД2

1теория, лежащая в основе расчёта crc

Для начала давайте немного разберёмся в теории. Итак, что же такое CRC? Если кратко, это одна из разновидностей подсчёта контрольной суммы. Контрольная сумма – это метод проверки целостности принятой информации на стороне приёмника при передаче по каналам связи.

Например, одна из простейших проверок – использование бита чётности. Это когда суммируются все биты передаваемого сообщения, и если сумма оказывается чётной, то в конец сообщения добавляется 0, если нечётной – то 1. При приёме также подсчитывается сумма битов сообщения, и сравнивается с принятым битом чётности. Если они отличаются, значит при передаче возникли ошибки, и передаваемая информация была искажена.

По сути, CRC – это не сумма, а результат деления некого объёма информации (информационного сообщения) на константу, а точнее – остаток от деления сообщения на константу. Тем не менее, CRC исторически также называют «контрольная сумма». В значение CRC вносит вклад каждый бит сообщения.

То есть, если хотя бы один бит исходного сообщения изменится при передаче, контрольная сумма тоже изменится, причём существенно. Это большой плюс такой проверки, так как он позволяет однозначно определить, исказилось исходное сообщение при передаче или нет.

Что такое исходное сообщение – понятно. Это непрерывная последовательность битов произвольной длины.

Что за константа, на которую мы должны делить исходное сообщение? Это некоторое число также любой длины, но обычно используются числа, кратные 1 байту – 8, 16 или 32 бита. Просто так легче считать, ведь компьютеры работают именно с байтами, а не с битами.

Константу-делитель обычно записывают в виде полинома (многочлена) вот таким образом: x8 x2 x1 x0. Здесь степень числа “x” означает позицию бита-единицы в числе, начиная с нулевой, а старший разряд указывает на степень полинома и отбрасывается при интерпретации числа. То есть записанное ранее число – это не что иное как 100000111 в двоичной системе счисления.

Обычно при записи многочлена старший разряд подразумевается, но не пишется. То есть вышеуказанный многочлен можно было бы записать в двоичной системе как (1)00000111. В скобках я указал подразумеваемый старший разряд числа. Поэтому говорят, что многочлен равен 7 в десятичной системе счисления (111b = 7d).

Вот ещё пример: (x16 ) x15 x2 x0 = (1)1000000000000101 = 0x8005 = 32773.

Обычно используются некие стандартные многочлены для разных типов CRC. Вот некоторые из них:

Алгоритм CRCОбразующий многочлен
CRC-160x8005
CRC-16-CCITT0x1021
CRC-16-DNP0x3D65
CRC-32-IEEE 802.30x04C11DB7
CRC-32C0x1EDC6F41
CRC-32K0x741B8CD7

В посвящённой расчёту CRC статье на Википедии есть большая таблица образующих полиномов.

Так как же считать контрольную сумму? Существует базовый метод – деление сообщения на полином «в лоб» – и его модификации в целях уменьшения количества вычислений и, соответственно, ускорения расчёта CRC. Для начала мы рассмотрим именно базовый метод.

В общем виде деление числа на многочлен выполняется по такому алгоритму. Алгоритм вычисления контрольной суммы CRC:

  1. Создаётся массив (регистр), заполненный нулями, равный по длине разрядности (степени) полинома.
  2. Исходное сообщение дополняется нулями в младших разрядах, в количестве, равном числу разрядов полинома.
  3. В младший разряд регистра заносится один старший бит сообщения, а из старшего разряда регистра выдвигается один бит.
  4. Если выдвинутый бит равен “1”, то производится инверсия битов (операция XOR, исключающее ИЛИ) в тех разрядах регистра, которые соответствуют единицам в полиноме.
  5. Если в сообщении ещё есть биты, переходим к шагу 3).
  6. Когда все биты сообщения поступили в регистр и были обработаны этим алгоритмом, в регистре остаётся остаток от деления, который и является контрольной суммой CRC.

Назовём этот метод расчёта CRC метод побитового сдвига или простой метод.

Рисунок иллюстрирует деление исходной последовательности битов на число (1)00000111, или многочлен x8 x2 x1 x0.

Схематичное представление вычисления CRC
Схематичное представление вычисления CRC на примере деления на многочлен x8 x2 x1 x

Кстати, проверить правильность расчёта CRC очень просто. В пункте (2) описанного алгоритма мы должны вместо дополнения исходного сообщения нулями дополнить его битами рассчитанной контрольной суммы, а остальное оставить как есть. Теперь остаток от деления дополненного сообщения на полином должен равняться нулю – это и есть признак верно рассчитанной контрольной суммы. Отличный от нуля остаток свидетельствует об ошибке.

Осталась ещё пара моментов, о которых стоит сказать. Как вы могли заметить, сообщение можно разделить на любое число. Как его выбрать? Существует ряд стандартных полиномов, которые используются при вычислении CRC. Например, для CRC32 это может быть число 0x04C11DB7, а для CRC16 это может быть 0x8005.

Кроме того, в регистр в начале вычислений можно записать не нули, а какое-то другое число. (И это рекомендуется делать: так повышается надёжность определения начала передачи сообщения, если, например, сообщение имеет в начале нулевые биты).

Также при расчётах непосредственно перед выдачей финальную контрольную сумму CRC можно поделить на какое-то другое число.

И последнее. Байты сообщения при записи в регистр могут помещаться как старшим битом «вперёд», так и наоборот, младшим. И результирующая CRC также может выдаваться, начиная со старшего бита или с младшего.

Изменение порядка битов в байте на обратный назовём «обращение», «реверс» или «отзеркаливание» байта.

Итого имеются 6 параметров, которые влияют на значение контрольной суммы:

  • порядок CRC;
  • образующий многочлен (его иногда называют «генераторный полином», переводя с английского буквально);
  • начальное содержимое регистра;
  • значение, с которым производится финальное XOR;
  • реверс байтов информационного сообщения;
  • реверс байтов CRC перед финальным XOR.

2программный код для отсевагрубых погрешностей в малой выборке

Основной метод класса – RemoveOutliers(), который получает в качестве входного аргумента массив целых значений (исходный ряд наблюдений), а возвращает массив с исключёнными грубыми погрешностями. Минимальная длина исходной выборки – 3 элемента, максимальная, как правило – не более 52.

Второй используемый метод – StdDeviation() – расчёт среднеквадратичного отклонения. Этот метод также получает на вход массив целых чисел и (опционально) – признак, использовать ли смещённую оценку СКО или несмещённую.

Отсеивание грубых погрешностей на C#
using System;
using System.Linq;
using System.Collections.Generic;

public class Stat
{
  public static int[] RemoveOutliers(int[] data)
  {
    if (data.Length < 3)
    {
      throw new ArgumentException("Вектор должен содержать хотя бы 3 элемента.");
    }
    List<int> resList = new List<int>(data);
    while (true)
    {
      double elemMean = ((IEnumerable<int>) resList).Average();
      double elemStdDeviation = StdDeviation(resList.ToArray(), false);
      int minElem = ((IEnumerable<int>) resList).Min();
      int maxElem = ((IEnumerable<int>) resList).Max();
      double tau1 = (elemMean - minElem) / elemStdDeviation;
      double tauN = (maxElem - elemMean) / elemStdDeviation;
      double tauCritical = 0.0;
      int numMeasurements = resList.Count;
      if (numMeasurements < 40)
      {
          tauCritical = (2.4   (((double) numMeasurements) / 57.0)) - (4.0 / ((double) numMeasurements));
      }
      else
      {
          tauCritical = 3.0;
      }
      if ((tau1 <= tauCritical) && (tauN <= tauCritical))
      {
          return resList.ToArray();
      }
      if (tau1 >= tauN)
      {
          int minElemIndex = resList.IndexOf(minElem);
          resList.RemoveAt(minElemIndex);
      }
      else
      {
          int maxElemIndex = resList.IndexOf(maxElem);
          resList.RemoveAt(maxElemIndex);
      }
    }
  }
  
  public static double StdDeviation(int[] data, bool isShifted = false)
  {
    double meanX = data.Average();
    double squaresSum = 0.0;
    foreach (int currentX in data)
    {
      double s = currentX - meanX;
      squaresSum  = s * s;
    }
    int n = data.Length - 1;
    if (isShifted)
    {
      n  ;
    }
    return Math.Sqrt(squaresSum / ((double) n));
  }
}

Тот же код, переписанный на VB.NET, представлен ниже.

Отсеивание грубых погрешностей на VB.NET
Imports System
Imports System.Linq
Imports System.Collections.Generic

Public Class Stat

''' <summary>
''' Исключает из малой выборки (от 3-х элементов и выше) данные с резко отличающимися значениями (грубые погрешности).
''' </summary>
''' <param name="data">Массив данных, которые нужно проверить на предмет резко отличающихся значений. Минимальный набор – 3 элемента, максимальный – 52.</param>
''' <returns>Массив данных без резко отличающихся значений.</returns>
''' <remarks>Есть массив значений <paramref name="data">data()</paramref>. Считается, что значения элементов подчинены нормальному закону распределения. Необходимо исключить из массива данные с резко отличающимися значениями.</remarks>
Public Shared Function RemoveOutliers(ByVal data() As Integer) As Integer()

  If data.Length < 3 Then
    Throw New ArgumentException("Выборка должна содержать хотя бы 3 элемента.")
  End If
  
  Dim resList As New List(Of Integer)(data)
  
  Do
    'Определяем оценки среднего значения и среднеквадратичного отклонения, минимальное и максимальное значения:
    Dim elemMean As Double = resList.Average
    Dim elemStdDeviation As Double = StdDeviation(resList.ToArray)
    Dim minElem As Integer = resList.Min
    Dim maxElem As Integer = resList.Max
    
    'Определяем критическое значение тау-статистики:
    Dim tauCritical As Double = 0.0
    Dim numMeasurements As Integer = resList.Count
    If numMeasurements < 40 Then
      tauCritical = 2.4   numMeasurements / 57 - 4.0 / numMeasurements
    Else
      tauCritical = 3
    End If
    
    'Для найденных экстремальных значений определяем тау-статистики:
    Dim tau1 As Double = (elemMean - minElem) / elemStdDeviation
    Dim tauN As Double = (maxElem - elemMean) / elemStdDeviation
    
    'Проверяем вектор на наличие резко отличающихся значений:
    If (tau1 > tauCritical) OrElse (tauN > tauCritical) Then
      'Удаляем экстремальный элемент:
      If tau1 >= tauN Then
        Dim minElemIndex As Integer = resList.IndexOf(minElem)
        resList.RemoveAt(minElemIndex)
      Else
        Dim maxElemIndex As Integer = resList.IndexOf(maxElem)
        resList.RemoveAt(maxElemIndex)
      End If
    Else
      Return resList.ToArray
    End If
  Loop

End Function

''' <summary>
''' Возвращает среднеквадратическое отклонение последовательности.
''' </summary>
''' <param name="data">Массив данных, по которым вычисляется СКО.</param>
''' <param name="isShifted">Нужна ли несмещённая или смещённая оценка СКО. По умолчанию – несмещённая.</param>
Public Shared Function StdDeviation(ByVal data As Integer(), Optional ByVal isShifted As Boolean = False) As Double

  Dim meanX As Double = data.Average
  
  Dim squaresSum As Double = 0
  For Each currentX As Integer In data
    Dim s As Double = currentX - meanX
    squaresSum  = s * s
  Next
  
  Dim n As Integer = data.Length - 1
  If isShifted Then n  = 1
  
  Dim res As Double = System.Math.Sqrt(squaresSum / n)
  Return res

End Function

End Class

2расчёт контрольной суммы crc методом побитового сдвига

На основании всего вышеизложенного, давайте напишем функцию на языке Visual Basic .NET, которая будет рассчитывать контрольную сумму CRC, принимая ряд параметров, которые я описал выше, и возвращая значение CRC в виде 32-разрядного беззнакового числа.

Код ошибки:  obd2 subaru на АлиЭкспресс — купить онлайн по выгодной цене
Код расчёта CRC методом побитового сдвига на языке VB.NET
''' <summary>
''' Возвращает контрольную сумму типа CRC, рассчитанную методом побитового сдвига.
''' </summary>
''' <param name="bytes">Входная последовательность байтов (исходное сообщение).</param>
''' <param name="poly">Образующий многочлен разрядности <paramref name="width">width</paramref>.</param>
''' <param name="width">Порядок CRC в битах, 8/16/32.</param>
Public Shared Function GetCrc_Simple(ByVal bytes As Byte(), ByVal poly As UInteger, Optional ByVal width As Integer = 32, Optional ByVal initReg As UInteger = &HFFFFFFFFUI, Optional ByVal finalXor As UInteger = &HFFFFFFFFUI, Optional ByVal reverseBytes As Boolean = True, Optional ByVal reverseCrc As Boolean = True) As UInteger

  Dim widthInBytes As Integer = width  8
  
  'Дополняем сообщение width нулями (расчёт в байтах):
  ReDim Preserve bytes(bytes.Length - 1   widthInBytes)
  
  'Создаём очередь битов из сообщения:
  Dim msgFifo As New Queue(Of Boolean)(bytes.Count * 8 - 1)
  For Each b As Byte In bytes
    Dim ba As New BitArray({b})
    If reverseBytes Then
      For i As Integer = 0 To 7
          msgFifo.Enqueue(ba(i))
      Next
    Else
      For i As Integer = 7 To 0 Step -1
          msgFifo.Enqueue(ba(i))
      Next
    End If
  Next
  
  'Создаём очередь из битов начального заполнения регистра:
  Dim initBytes As Byte() = BitConverter.GetBytes(initReg)
  Dim initBytesReversed As IEnumerable(Of Byte) = (From b As Byte In initBytes Take widthInBytes).Reverse
  Dim initFifo As New Queue(Of Boolean)(width - 1)
  For Each b As Byte In initBytesReversed
    Dim ba As New BitArray({b})
    If Not reverseBytes Then
       For i As Integer = 0 To 7
           initFifo.Enqueue(ba(i))
       Next
    Else
      For i As Integer = 7 To 0 Step -1
          initFifo.Enqueue(ba(i))
      Next
    End If
  Next
  
  'Сдвиг и XOR:
  Dim register As UInteger = 0 'заполняем width-разрядный регистр нулями.
  Do While msgFifo.Count > 0
    
    Dim poppedBit As Integer = CInt(register >> (width - 1)) And 1 'определить перед сдвигом регистра.
    
    Dim shiftedBit As Byte = Convert.ToByte(msgFifo.Dequeue)
    If initFifo.Count > 0 Then
      Dim b As Byte = Convert.ToByte(initFifo.Dequeue)
      shiftedBit = shiftedBit Xor b
    End If
    
    register = register << 1
    register = register Or shiftedBit
    
    If poppedBit = 1 Then
      register = register Xor poly
    End If
  Loop
  
  'Финальные преобразования:
  Dim crc As UInteger = register 'Регистр содержит остаток от деления - контрольную сумму.
  If reverseCrc Then
    crc = reflect(crc, width)
  End If
  crc = crc Xor finalXor
  crc = crc And (&HFFFFFFFFUI >> (32 - width)) 'маскируем младшие разряды.
  
  Return crc

End Function

''' <summary>
''' Обращает заданное число младших битов переданного числа.
''' </summary>
''' <param name="inpValue">Число, которое требуется «отзеркалить».</param>
''' <param name="bitsToReflect">Сколько младших битов обратить, 0..32.</param>
''' <returns></returns>
''' <remarks>Например: reflect(&H3E23, 3) == &H3E26.</remarks>
Private Shared Function reflect(ByVal inpValue As UInteger, Optional ByVal bitsToReflect As Integer = 32) As UInteger
    Dim t As UInteger = inpValue
    Dim reflected As UInteger = inpValue
    For i As Integer = 0 To bitsToReflect - 1
        Dim bm As UInteger = bitMask(bitsToReflect - 1 - i)
        If (t And 1) = 1 Then
            reflected = reflected Or bm
        Else
            reflected = reflected And Not bm
        End If
        t >>= 1
    Next
    Return reflected
End Function

''' <summary>
''' Возвращает наибольший разряд числа.
''' </summary>
''' <param name="number">Число, разрядность которого следует определить.</param>
''' <returns></returns>
Private Shared Function bitMask(ByVal number As Integer) As UInteger
    Dim res As UInteger = (1UI << number)
    Return res
End Function

Как вы могли заметить, в данной реализации расчёта CRC используется LINQ, так что соответствующая ссылка должна быть добавлена в проект.

Предлагаемая программа плохо масштабируема. То есть она работает хорошо при вычислении контрольной суммы CRC для коротких сообщений, длиной до нескольких десятков килобайтов. Я писал её с целью только продемонстрировать работу простого алгоритма, и не занимался оптимизацией.

При расчёте CRC для длинного сообщения, размером десятки или сотни мегабайтов, программа будет сильно загружать процессор и память, т.к. всё сообщение целиком загружается в очередь. Этому способствует метод преобразования числа в битовую последовательность, используя Queue(Of Boolean).

Зато у этой программы есть одно преимущество: она может быть использована для расчёта CRC любого порядка, не обязательно 8, 16 или 32. Это может быть CRC5 или CRC49. Только для чисел больше 32-х разрядов нужно изменить соответствующим образом входные параметры – допустим, poly передавать не как UInteger, а как ULong, или передавать его в виде битового массива (тогда теоретически порядок CRC вообще будет неограничен).

3 работа с датчиком давления и температуры bmp280 по интерфейсу spi

Микросхема FT2232 работает только в режиме ведущего (Master). Для того чтобы проверить наш код в действии, нам нужно ведомое устройство (Slave). Хорошим кандидатом на роль ведомого будет датчик давления и температуры BMP280, который может работать по интерфейсу I2C или SPI (3- или 4-проводной).

Модуль с датчиком температуры и давления BMP280
Модуль с датчиком температуры и давления BMP280

Если мы посмотрим на карту регистров данного датчика, то увидим один регистр, который содержит постоянное значение – это регистр по адресу 0xD0 с именем id. В нём хранится идентификатор датчика, равный 0x58. Именно это число мы должны прочитать, если обратимся к регистру id. Давайте проверим это.

Карта памяти датчика BMP280
Карта памяти датчика BMP280

Изображения взяты из технического описания (datasheet) на датчик BMP280. В конце статьи даются ссылки на скачивание datasheet BMP280.

Протокол обмена датчика BMP280 по SPI следующий:

Протокол SPI датчика давления BMP280
Протокол SPI датчика давления BMP280

Линия CS активна при низком уровне. Первый передаваемый бит (RW) определяет чтение или запись. В нашем случае он должен быть равен “1” (чтение). Далее следуют 7 бит адреса регистра (AD6…AD0), с которого начинаем чтение. Далее генерируется столько тактовых импульсов, сколько битов мы хотим прочитать из датчика.

Чтение последовательности байтов из датчика BMP280
Чтение последовательности байтов из датчика BMP280

Датчик сам следит за указателем на текущий регистр, поэтому если мы начали читать с адреса 0x76, следующий запрошенный байт будет прочитан из регистра 0x77, и т.д.

Подключим датчик BMP280 к первому каналу микросхемы FT2232H вот по такой схеме:

Схема соединения BMP280 с FT2232H
Вывод BMP280Вывод FT2232HПримечание
SCLADBUS0если используется первый канал
CSBADBUS3если используется первый канал
SDAADBUS1если используется первый канал
SDOADBUS2если используется первый канал
VCCнеттребуется отдельное питание 3.3 В
GNDGNDсоединить с GND источника 3.3 В

Если используется канал, отличный от первого, то соответствие выводов смотрите в техническом описании микросхемы. Например, у FT232 один канал, у FT2232 два канала, а у FT4232 – четыре.

Вывода с 3,3 вольтами у микросхемы FT2232 нет, поэтому для обеспечения питания датчику BMP280 придётся где-то его взять. Можно, к примеру, использовать плату Arduino, у которой имеется необходимое напряжение. Только не забудьте объединить GND датчика, Arduino и FT2232. Я, например, так и сделал:

Подключение датчика BMP280 к FT2232, питание подаётся от 3,3V Arduino Nano
Подключение датчика BMP280 к FT2232, питание подаётся от 3,3V Arduino Nano

Итак, давайте же прочитаем наш регистр. Для этого опускаем линию CS в LOW и записываем в BMP280 число 0xD0 – адрес регистра id (вызываем метод с параметрами SpiWrite(&HD, SPI_TRANSFER_OPTIONS.CHIPSELECT_ENABLE). Линию CS пока не поднимаем.

Временная диаграмма чтения регистра идентификатора датчика BMP280
Временная диаграмма чтения регистра идентификатора датчика BMP280

Такое же значение, как на диаграмме, возвращает метод SpiRead() – 0x58. Поздравляю, мы только что прочитали регистр идентификатора сенсора BMP280. А это значит, что наш код работает корректно.

Для желающих потренироваться в дальнейшем освоении интерфейса SPI – датчик BMP280 хороший «подопытный» 🙂

3декодер кода хэмминга (15, 11), написанный на vb.net

Теперь пора поговорить о декодере. Декодер получает на вход 2 байта закодированных данных и возвращает 11 бит декодированных данных, которые распределены по двум байтам. Если в кодер были переданы 8 бит данных, то нас будет интересовать только первый байт, полученный с декодера.

Код декодера Хэмминга (15, 11) на VB.NET (разворачивается)
''' <summary>
''' Декодер кода (15, 11). 
''' </summary>
''' <param name="b">Входные данные, 16 бит (2 байта).</param>
''' <returns>Выходные данные – 10 бит.</returns>
''' <remarks>
''' Размещение проверочных и информационных бит в кодовом слове:
''' 
'''          |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
''' code_word| L| K| I| H| G| F| E| P| D| C| B| P| A| P| P| X|
''' out_data |  |  |  |  |  | L| K| I| H| G| F| E| D| C| B| A|
''' 
''' A,B,C,D,E,F,G,H,I,K,L – биты данных информационного слова;
'''                     P – проверочный бит;
'''                     X – бит, равный 0 (не используется).
''' 
''' Кодовое слово дополняется одним битом, чтобы длина была равна степени двойки. Сейчас этот бит никак не используется.
''' Можно его использовать как бит четности и получить так называемый дополненный код Хэмминга. Здесь этого не сделано.
''' </remarks>
Public Shared Function Decode_15_11(ByVal b As Byte()) As Integer

    Dim codeWord As New BitArray(b) '16 бит входных данных

    'Весь процесс декодирования – это сложение по модулю два бит информационного слова, по весу полученных единиц в результате – получение позиции ошибки.
    Dim syndrome As New BitArray(4) 

    'Вычисление первого проверочного символа из полученного кодового слова и далее сравнение его с полученным.
    syndrome(0) = codeWord(3) Xor codeWord(5) Xor codeWord(7) Xor codeWord(9) Xor codeWord(11) Xor codeWord(13) Xor codeWord(15) Xor codeWord(1)

    'Вычисление второго проверочного символа из полученного кодового слова и далее сравнение его с полученным.
    syndrome(1) = codeWord(3) Xor codeWord(6) Xor codeWord(7) Xor codeWord(10) Xor codeWord(11) Xor codeWord(14) Xor codeWord(15) Xor codeWord(2)

    'Вычисление третьего проверочного символа из полученного кодового слова и далее сравнение его с полученным.
    syndrome(2) = codeWord(5) Xor codeWord(6) Xor codeWord(7) Xor codeWord(12) Xor codeWord(13) Xor codeWord(14) Xor codeWord(15) Xor codeWord(4)

    'Вычисление четвёртого проверочного символа из полученного кодового слова и далее сравнение его с полученным.
    syndrome(3) = codeWord(9) Xor codeWord(10) Xor codeWord(11) Xor codeWord(12) Xor codeWord(13) Xor codeWord(14) Xor codeWord(15) Xor codeWord(8)

    'Вычисление по синдрому позиции ошибки. Это просто ПЗУ или дешифратор.
    'Если смотреть на синдром как на число - то это и есть номер позиции ошибки.
    'Синдром равен 0 - ошибки нет.
    'Поскольку на выход модуля передаются только биты данных - не все варианты перечислены, нет смысла исправлять проверочные биты.
    Dim syn As Integer = (Convert.ToInt32(syndrome(3)) << 3) Or (Convert.ToInt32(syndrome(2)) << 2) Or (Convert.ToInt32(syndrome(1)) << 1) Or Convert.ToInt32(syndrome(0))

    '         |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
    'code_word| L| K| I| H| G| F| E| P| D| C| B| P| A| P| P| X|
    'Синдромы ошибок в информационных битах, позиции в кодовом слове 3,5,6,7,9,10,11,12,13,14,15:
    Dim correction As New BitArray(11) 
    Select Case syn
        Case 3 'позиция 3
            correction = New BitArray({True, False, False, False, False, False, False, False, False, False, False}) 
        Case 5 'позиция 5
            correction = New BitArray({False, True, False, False, False, False, False, False, False, False, False}) 
        Case 6 'позиция 6
            correction = New BitArray({False, False, True, False, False, False, False, False, False, False, False}) 
        Case 7 'позиция 7
            correction = New BitArray({False, False, False, True, False, False, False, False, False, False, False}) 
        Case 9 'позиция 9
            correction = New BitArray({False, False, False, False, True, False, False, False, False, False, False}) 
        Case 10 'позиция 10
            correction = New BitArray({False, False, False, False, False, True, False, False, False, False, False})  
        Case 11 'позиция 11
            correction = New BitArray({False, False, False, False, False, False, True, False, False, False, False})  
        Case 12 'позиция 12
            correction = New BitArray({False, False, False, False, False, False, False, True, False, False, False})  
        Case 13 'позиция 13
            correction = New BitArray({False, False, False, False, False, False, False, False, True, False, False})  
        Case 14 'позиция 14
            correction = New BitArray({False, False, False, False, False, False, False, False, False, True, False})  
        Case 15 'позиция 15
            correction = New BitArray({False, False, False, False, False, False, False, False, False, False, True})  
        Case Else
            'Если синдром равен 0 или указывает на ошибку в проверочном символе "P" - коррекция информационных символов не требуется.
            'Мы инициализировали correction() нулями (correction = New BitArray(11)), поэтому ничего делать не нужно.
        End Select

        'Результат декодирования с учетом коррекции (11 бит выходных данных):
        Dim outData As New BitArray(11)
        outData(0) = codeWord(3) Xor correction(0)    
        outData(1) = codeWord(5) Xor correction(1)     
        outData(2) = codeWord(6) Xor correction(2)     
        outData(3) = codeWord(7) Xor correction(3)     
        outData(4) = codeWord(9) Xor correction(4)     
        outData(5) = codeWord(10) Xor correction(5)   
        outData(6) = codeWord(11) Xor correction(6)  
        outData(7) = codeWord(12) Xor correction(7) 
        outData(8) = codeWord(13) Xor correction(8) 
        outData(9) = codeWord(14) Xor correction(9) 
        outData(10) = codeWord(15) Xor correction(10) 

        Dim masks(31) As Integer
        masks(0) = BitVector32.CreateMask()
        For i As Integer = 1 To 31
            masks(i) = BitVector32.CreateMask(masks(i - 1))
        Next

        Dim v As New BitVector32
        For i As Integer = 0 To 10
            v(masks(i)) = outData(i)
        Next
        Dim decoded As Integer = v.Data

        Return decoded

End Function

3пример программы машинного обученияна vb.net

Возьмём пример из блога компании Microsoft на Хабре, немного изменим и адаптируем его для VB.NET (а также исправим ошибку в приведённом C# коде). Скачаем файл с данными для обучения модели. Его нужно положить в директорию /bin проекта.

Код ошибки:  elm-typescript-types-comparison.md · GitHub

Или можно поставить в свойствах файла через проводник по решению указать действие при компиляции – копировать в выходную директорию. Далее создадим проект консольного приложения .NET Core, переименуем файл Module1.vb в Program.vb (не обязательно) и напишем в нём следующий код:

Imports Microsoft.ML
Imports Microsoft.ML.Data
Imports Microsoft.ML.Transforms

Module Program

    'Шаг 1: Определите ваши структуры данных

    ''' <summary>
    ''' Используется для предоставления обучающих данных, а также как введение для предиктивных операций.
    ''' </summary>
    Public Class IrisData

        ''' <summary>
        ''' Первые 4 свойства - это входные данные / функции, используемые для прогнозирования метки label.
        ''' </summary>
        <LoadColumn(0)>
        Public SepalLength As Single

        <LoadColumn(1)>
        Public SepalWidth As Single

        <LoadColumn(2)>
        Public PetalLength As Single

        <LoadColumn(3)>
        Public PetalWidth As Single

        ''' <summary>
        ''' Label - это то, что вы предсказываете, и устанавливается только при обучении.
        ''' </summary>
        <LoadColumn(4)>
        Public Label As String
        
    End Class

    'Результат операции прогнозирования.
    Public Class IrisPrediction

        <ColumnName("PredictedLabel")>
        Public PredictedLabels As String

    End Class


    Sub Main(args As String())
        
        ' Шаг 2: Создание среды ML.NET 
        Dim mlContext As New MLContext()

        Dim reader As TextLoader = mlContext.Data.CreateTextLoader(Of IrisData)(separatorChar:=",", hasHeader:=False)
        Dim trainingDataView As IDataView = reader.Load("iris.data")
        Console.WriteLine($"Данные из файла ""iris.data"" прочитаны.")

        ' Шаг 3: Преобразуйте свои данные и добавьте learner:

        ' Присвойте числовые значения тексту в столбце "label", потому что только числа могут быть обработаны во время обучения модели.
        ' Добавьте обучающий алгоритм в pipeline. 
        ' Преобразовать label обратно в исходный текст (после преобразования в число на шаге 3).
        Dim pipeline As EstimatorChain(Of KeyToValueMappingTransformer) = mlContext.Transforms.Conversion.MapValueToKey("Label") _
            .Append(mlContext.Transforms.Concatenate("Features", "SepalLength", "SepalWidth", "PetalLength", "PetalWidth")) _
            .Append(mlContext.MulticlassClassification.Trainers.SdcaNonCalibrated("Label", "Features")) _
            .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel"))

        ' Шаг 4: Обучите модель на этом наборе данных:
        
        Console.WriteLine("Начато обучение модели.")
        Dim model As TransformerChain(Of KeyToValueMappingTransformer) = pipeline.Fit(trainingDataView)
        Console.WriteLine("Обучение модели завершено.")

		' Шаг 5: Используйте модель для предсказания:
        Do
            Console.WriteLine()
            Dim id As New IrisData()
            Console.Write("Длина чашелистника (sepal length) = ") 
            id.SepalLength = CSng(Console.ReadLine())
            Console.Write("Ширина чашелистника (sepal width) = ") 
            id.SepalWidth = CSng(Console.ReadLine())
            Console.Write("Длина лепестка (petal length) = ") 
            id.PetalLength = CSng(Console.ReadLine())
            Console.Write("Ширина лепестка (petal width) = ") 
            id.PetalWidth = CSng(Console.ReadLine())
            Dim prediction As IrisPrediction = GetPrediction(mlContext, model, id)
            Console.WriteLine($"Предсказанный тип цветка: {prediction.PredictedLabels}")
        Loop
    End Sub

    Private Function GetPrediction(mlContext As MLContext, model As TransformerChain(Of KeyToValueMappingTransformer), data As IrisData) As IrisPrediction
        Dim prediction As IrisPrediction = mlContext.Model.CreatePredictionEngine(Of IrisData, IrisPrediction)(model).Predict(data)
        Return prediction
    End Function

End Module

Запустим нашу программу с машинным обучением. Если задавать разные значения длины и ширины чашелистника и лепестка ириса, то увидим примерно следующее:

Результат работы алгоритма машинного обучения на ML.NET и Visual Basic
Результат работы алгоритма машинного обучения на ML.NET и Visual Basic

Как видно, модель обучилась на тестовых данных, и теперь мы можем по данным о длине и ширине чашелистника и лепестка с определённой вероятностью предсказывать вид ириса.

3расчёт контрольной суммы crc табличным методом

Для сокращения числа вычислений из предыдущего метода – метода побитового сдвига – придуманы некоторые оптимизации.

В частности, сдвигают не по одному биту за раз, а сразу по несколько. Наибольшую популярность снискали варианты, в которых сообщение сдвигается на число битов, кратное числу битов в байте: 8, 16 или 32, т.к. с байтами легче работать (не нужны дополнительные преобразования). При этом идея алгоритма осталась та же: сдвиг и исключающее ИЛИ с содержимым регистра.

Кроме того, оказывается, что часть расчётов можно провести заранее и записать в массив – таблицу, из которой по мере необходимости будет браться нужное число. Такой метод расчёта назвали табличный метод расчёта CRC.

Я не буду здесь вдаваться в теорию, она довольно сложна и много раз описана в других статьях. В частности, очень хорошее и подробное описание бинарной арифметики, лежащей в основе расчёта CRC, и описание табличного метода, даётся в статье Ross N. Williams:

Код ошибки:  Распиновка разъема gm12 ваз

Ну что же, пришло время для самой программы. Она будет несколько длиннее предыдущей. По сути, это реализация алгоритма из указанной статьи в стиле объектно-ориентированного программирования. Опять же будем писать программу на моём любимом языке программирования VB.NET. Я назвал этот класс RocksoftCrcModel, по названию компании, в которой работал автор указанной статьи.

Код расчёта CRC табличным методом на языке VB.NET
    ''' <summary>
    ''' Реализует алгоритм расчёта CRC методом Rocksoft^tm Model CRC.
    ''' </summary>
    Public Class RocksoftCrcModel

#Region "PROPS AND FIELDS"

        ''' <summary>
        ''' Таблица предвычисленных значений для расчёта контрольной суммы.
        ''' </summary>
        Public ReadOnly CrcLookupTable(255) As UInteger

        ''' <summary>
        ''' Порядок CRC, в битах (строго 8, 16 или 32).
        ''' Изменение этого свойства ведёт к пересчёту таблицы.
        ''' </summary>
        Public Property CrcWidth As Integer
            Get
                Return _CrcWidth
            End Get
            Set(value As Integer)
                If _CrcWidth <> value Then
                    _CrcWidth = value
                    _TopBit = getBitMask(_CrcWidth - 1)
                    _WidMask = (((1UI << (_CrcWidth - 1)) - 1UI) << 1) Or 1UI
                    generateLookupTable()
                End If
            End Set
        End Property
        Private _CrcWidth As Integer = 32

        ''' <summary>
        ''' Образующий многочлен.
        ''' Изменение этого свойства ведёт к пересчёту таблицы.
        ''' </summary>
        Public Property Polynom As UInteger
            Get
                Return _Polynom
            End Get
            Set(value As UInteger)
                If _Polynom <> value Then
                    _Polynom = value
                    generateLookupTable()
                End If
            End Set
        End Property
        Private _Polynom As UInteger = &H4C11DB7

        ''' <summary>
        ''' Обращать ли байты сообщения?
        ''' Изменение этого свойства ведёт к пересчёту таблицы.
        ''' </summary>
        Public Property ReflectIn As Boolean
            Get
                Return _ReflectIn
            End Get
            Set(value As Boolean)
                If _ReflectIn <> value Then
                    _ReflectIn = value
                    generateLookupTable()
                End If
            End Set
        End Property
        Private _ReflectIn As Boolean = True

        ''' <summary>
        ''' Начальное содержимое регистра.
        ''' </summary>
        Public Property InitRegister As UInteger
            Get
                Return _InitRegister
            End Get
            Set(value As UInteger)
                If _InitRegister <> value Then
                    _InitRegister = value
                End If
            End Set
        End Property
        Private _InitRegister As UInteger = &HFFFFFFFFUI

        ''' <summary>
        ''' Обращать выходное значение CRC?
        ''' </summary>
        Public Property ReflectOut As Boolean
            Get
                Return _ReflectOut
            End Get
            Set(value As Boolean)
                If _ReflectOut <> value Then
                    _ReflectOut = value
                End If
            End Set
        End Property
        Private _ReflectOut As Boolean = True

        ''' <summary>
        ''' Значение, с которым XOR-ится выходное значение CRC.
        ''' </summary>
        Public Property XorOut As UInteger
            Get
                Return _XorOut
            End Get
            Set(value As UInteger)
                If _XorOut <> value Then
                    _XorOut = value
                End If
            End Set
        End Property
        Private _XorOut As UInteger = &HFFFFFFFFUI

#End Region '/PROPS AND FIELDS

#Region "READ-ONLY PROPS"

        ''' <summary>
        ''' Возвращает старший разряд полинома.
        ''' </summary>
        ReadOnly Property TopBit As UInteger
            Get
                Return _TopBit
            End Get
        End Property
        Private _TopBit As UInteger = getBitMask(CrcWidth - 1)

        ''' <summary>
        ''' Возвращает длинное слово со значением (2^width)-1.
        ''' </summary>
        Private ReadOnly Property WidMask As UInteger
            Get
                Return _WidMask
            End Get
        End Property
        Private _WidMask As UInteger = (((1UI << (CrcWidth - 1)) - 1UI) << 1) Or 1UI

#End Region '/READ-ONLY PROPS

#Region "CTOR"

        ''' <summary>
        ''' Конструктор, инициализированный параметрами по умолчанию для алгоритма CRC32.
        ''' </summary>
        Public Sub New()
            generateLookupTable()
        End Sub

        ''' <summary>
        ''' Инициализирует новый экземпляр параметрической модели CRC с настраиваемыми параметрами.
        ''' </summary>
        ''' <param name="width">Разрядность контрольной суммы в битах.</param>
        ''' <param name="poly">Полином.</param>
        ''' <param name="initReg">начальное содержимое регистра.</param>
        ''' <param name="isReflectIn">Обращать ли входящие байты сообщения?</param>
        ''' <param name="isReflectOut">Обратить ли CRC перед финальным XOR.</param>
        ''' <param name="xorOut">Конечное значение XOR.</param>
        Public Sub New(ByVal width As Integer, ByVal poly As UInteger, Optional ByVal initReg As UInteger = &HFFFFFFFFUI, Optional ByVal isReflectIn As Boolean = True, Optional ByVal isReflectOut As Boolean = True, Optional ByVal xorOut As UInteger = &HFFFFFFFFUI)
            Me.CrcWidth = width
            Me.Polynom = poly
            Me.InitRegister = initReg
            Me.ReflectIn = isReflectIn
            Me.ReflectOut = isReflectOut
            Me.XorOut = xorOut
            generateLookupTable()
        End Sub

#End Region '/CTOR

#Region "ВЫЧИСЛЕНИЕ CRC"

        ''' <summary>
        ''' Вычисляет значение контрольной суммы переданного сообщения.
        ''' </summary>
        ''' <param name="message">Исходное сообщение, для которого нужно посчитать контрольную сумму.</param>
       Public Function ComputeCrc(ByRef message As Byte()) As UInteger
            Dim registerContent As UInteger = InitRegister 'Содержимое регистра в процессе пересчёта CRC.
            For Each b As Byte In message
                registerContent = getNextRegisterContent(registerContent, b)
            Next
            Dim finalCrc As UInteger = getFinalCrc(registerContent)
            Return finalCrc
        End Function

        ''' <summary>
        ''' Вычисляет значение контрольной суммы переданного сообщения и возвращает его в виде массива байтов.
        ''' </summary>
        ''' <param name="message">Исходное сообщение, для которого нужно посчитать контрольную сумму.</param>
        Public Function ComputeCrcAsBytes(ByRef message As Byte()) As Byte()
            Dim crc As UInteger = ComputeCrc(message)
            Dim crcBytes As Byte() = BitConverter.GetBytes(crc)
            Dim crcBytesOrdered(crcBytes.Length - 1) As Byte
            For i As Integer = 0 To crcBytes.Length - 1
                crcBytesOrdered(i) = crcBytes(crcBytes.Length - 1 - i)
            Next
            Return crcBytesOrdered
        End Function

        ''' <summary>
        ''' Обрабатывает один байт сообщения (0..255).
        ''' </summary>
        ''' <param name="prevRegContent">Содержимое регистра на предыдущем шаге.</param>
        ''' <param name="value">Значение очередного байта из сообщения.</param>
        Private Function getNextRegisterContent(ByVal prevRegContent As UInteger, ByVal value As Byte) As UInteger
            Dim uValue As UInteger = value
            If ReflectIn Then
                uValue = reflect(uValue, 8)
            End If
            Dim reg As UInteger = prevRegContent
            reg = reg Xor (uValue << (CrcWidth - 8))
            For i As Integer = 0 To 7
                If (reg And TopBit) = TopBit Then
                    reg = (reg << 1) Xor Polynom
                Else
                    reg <<= 1
                End If
                reg = reg And WidMask()
            Next
            Return reg
        End Function

        ''' <summary>
        ''' Возвращает значение CRC для обработанного сообщения.
        ''' </summary>
        ''' <param name="regContent">Значение регистра до финального обращения и XORа.</param>
        Private Function getFinalCrc(ByVal regContent As UInteger) As UInteger
            If ReflectOut Then
                Dim res As UInteger = XorOut Xor reflect(regContent, CrcWidth)
                Return res
            Else
                Dim res As UInteger = XorOut Xor regContent
                Return res
            End If
        End Function

#End Region '/ВЫЧИСЛЕНИЕ CRC

#Region "РАСЧЁТ ТАБЛИЦЫ"

        ''' <summary>
        ''' Вычисляет таблицу предвычисленных значений для расчёта контрольной суммы.
        ''' </summary>
        Private Sub generateLookupTable()
            For i As Integer = 0 To 255
                CrcLookupTable(i) = generateTableItem(i)
            Next
        End Sub

        ''' <summary>
        ''' Рассчитывает один байт таблицы значений для расчёта контрольной суммы
        ''' по алгоритму Rocksoft^tm Model CRC Algorithm.
        ''' </summary>
        ''' <param name="index">Индекс записи в таблице, 0..255.</param>
        Private Function generateTableItem(ByVal index As Integer) As UInteger

            Dim inbyte As UInteger = CUInt(index)

            If ReflectIn Then
                inbyte = reflect(inbyte, 8)
            End If

            Dim reg As UInteger = inbyte << (CrcWidth - 8)

            For i As Integer = 0 To 7
                If (reg And TopBit) = TopBit Then
                    reg = (reg << 1) Xor Polynom
                Else
                    reg <<= 1
                End If
            Next

            If ReflectIn Then
                reg = reflect(reg, CrcWidth)
            End If

            Dim res As UInteger = reg And WidMask
            Return res

        End Function

#End Region '/РАСЧЁТ ТАБЛИЦЫ

#Region "ВСПОМОГАТЕЛЬНЫЕ"

        ''' <summary>
        ''' Возвращает наибольший разряд числа.
        ''' </summary>
        ''' <param name="number">Число, разрядность которого следует определить, степень двойки.</param>
        Private Function getBitMask(ByVal number As Integer) As UInteger
            Dim res As UInteger = 1UI << number
            Return res
        End Function

        ''' <summary>
        ''' Обращает заданное число младших битов переданного числа.
        ''' </summary>
        ''' <param name="value">Число, которое требуется обратить («отзеркалить»).</param>
        ''' <param name="bitsToReflect">Сколько младших битов числа обратить, 0..32.</param>
        ''' <remarks>Например: reflect(0x3E23, 3) == 0x3E26.</remarks>
        Private Function reflect(ByVal value As UInteger, Optional ByVal bitsToReflect As Integer = 32) As UInteger
            Dim t As UInteger = value
            Dim reflected As UInteger = value
            For i As Integer = 0 To bitsToReflect - 1
                Dim bm As UInteger = getBitMask(bitsToReflect - 1 - i)
                If (t And 1) = 1 Then
                    reflected = reflected Or bm
                Else
                    reflected = reflected And Not bm
                End If
                t >>= 1
            Next
            Return reflected
        End Function

#End Region '/ВСПОМОГАТЕЛЬНЫЕ

    End Class

Этот код полностью готов к использованию, можно брать и применять. Пользоваться данной программой так:

  • создать экземпляр класса RocksoftCrcModel(), передав в конструктор параметры модели CRC;
  • для расчёта контрольной суммы, вызвать метод данного объекта ComputeCrc() или ComputeCrcAsBytes(), передав в качестве параметра информационное сообщение, для которого необходимо посчитать контрольную сумму;
  • если меняются параметры модели CRC, таблица автоматически пересчитывается, и новый экземпляр класса можно не создавать.

Приведу пример использования данного класса для алгоритма CRC16. В качестве сообщения message будем использовать массив байтов, который представляет собой строку “123456789” в коде ASCII, которая используется во многих онлайн-калькуляторах CRC:

Оцените статью
OBD
Добавить комментарий

Adblock
detector