PDA

Просмотр полной версии : Контрольная сумма ТРМ201



_AS
15.08.2007, 15:00
Добрый день.

Подскажите пожалуйста алгоритм расчета контрольной суммы
для ТРМ201.

Например:

Запрос значения со входа:

Адрес 15

23 47 56 48 47 52 49 4с 4e 4d 4f 51 4b 0d
CS = 4d 4f 51 4b Как она получилась?

Дмитрий Егоренков
23.08.2007, 14:34
это пакет в ascii виде с начальным и конечным маркерами: #GVHGRILNMOQK\xD
после распаковки в двоичный вид получаем 0f 10 b2 57 68 a4, контрольная сумма - 68 a4, рассчитывается по следующему алгоритму:


unsigned short owenCRC16(unsigned char* packet, size_t length)
{
size_t i, j;
unsigned short crc;

assert(packet);

crc = 0;
for (i = 0; i < length; ++i)
{
unsigned char b = packet[i];
for (j = 0; j < 8; ++j, b <<= 1)
{
if ((b ^ (crc >> 8)) & 0x80)
{
crc <<= 1;
crc ^= 0x8F57;
}
else
crc <<= 1;
}
}
return crc;
}


подробности в описании протокола ОВЕН (http://www.owen.ru/support/40691149).

_AS
24.08.2007, 09:45
Спасибо большое.

Теперь работает!

незарегистрированный
31.01.2008, 21:53
это пакет в ascii виде с начальным и конечным маркерами: #GVHGRILNMOQK\xD
после распаковки в двоичный вид получаем 0f 10 b2 57 68 a4, контрольная сумма - 68 a4, рассчитывается по следующему алгоритму:


unsigned short owenCRC16(unsigned char* packet, size_t length)
{
size_t i, j;
unsigned short crc;

assert(packet);

crc = 0;
for (i = 0; i < length; ++i)
{
unsigned char b = packet[i];
for (j = 0; j < 8; ++j, b <<= 1)
{
if ((b ^ (crc >> 8)) & 0x80)
{
crc <<= 1;
crc ^= 0x8F57;
}
else
crc <<= 1;
}
}
return crc;
}


подробности в описании протокола ОВЕН (http://www.owen.ru/support/40691149).

Подобная проблема. Смотрел протокол но мало что понял.
В СИ я полный дуб, пробую силы на Делфи. Помогите пожалуйста, приведите пример на Делфи, или словами опишите алгоритм. Спасибо.

Дмитрий Егоренков
01.02.2008, 11:33
в дельфи я полный дуб, и вам на него время тратить не советую.

но если вы настаиваете, замените операторы >>, <<, ^, & на shr, shl, xor, and соответственно.

незарегистрированный
01.02.2008, 21:43
в дельфи я полный дуб, и вам на него время тратить не советую.

но если вы настаиваете, замените операторы >>, <<, ^, & на shr, shl, xor, and соответственно.

А говорите дуб... http://www.owen.ru/forum/images/icons/icon7.gif
Smile
Спасибо, попробую

Alexandr_Nikitin
26.02.2008, 12:14
Контрольная сумма!
Из приведеннова в форуме примера 23.08.2007, 14:34 помогите разобраться !
#GVHGRILNMOQK\xD после отбрасывания маркеров начала и конца кадра и перевода в двоичный код получилось:
00001111 0001000 10110010 01010111 01101000 10100100
Как считалась контрольная сумма? Объясните подробней с примером типа
00001111 + 0001000 + 10110010 01010111
На «С++» примеры и ссылки на протокол приводить не стоит! лучше на пальцах так понятней :)
Заранее благодарен!

Дмитрий Егоренков
26.02.2008, 13:24
вы меня извините, но на расчет crc у меня пальцев не хватит. алгоритм на с в этой ветке приведен. если вас интересует математическая основа, ее несложно найти в интернете.

если коротко, число состоящее из всех битов пакета делится на выбранный полином. crc - остаток от деления.

незарегистрированный
11.03.2008, 14:45
Попоробовал посчитать КС по алгоритму Дмитрия Егоренкова для такого запроса:
GGHIONOKGGGGRVSM получил 2e77, а на самом деле как видите bfc6.
В чем дело?

Дмитрий Егоренков
11.03.2008, 15:33
получил 2e77, а на самом деле как видите bfc6.
В чем дело?

не знаю. у меня получается bfc6.

mega
19.05.2010, 17:52
не знаю. у меня получается bfc6.

"ОПИСАНИЕ протокола обмена между ПЭВМ и приборами ОВЕН" не совсем корректное, особенно это касается "СТРУКТУРНОЙ СХЕМЫ ПРОТОКОЛА ОВЕН", вот и претензий много

я, честно говоря, не первый раз такие спецификации читаю и работаю с ними, поэтому когда писал конфигуратор команд для протокола owen, делал ровно то, что написано в этих документах,

а получилось это только после того, как увидел 2 "живые" команды на форуме

вот чтобы такого не происходило, я бы порекомендовал сделать всего 3 дополнения:
1. перерисуйте эту схему так, чтобы был виден четкий порядок бит, раз уж там они есть
2. ваш CRC расчитывается с исключением из пакета кроме маркеров еще и самой контрольной суммы, т.е. не текущая, не нулевая, а вообще без этого члена
3. в документации приведите 1-2 готовые команды, чтобы не ломать мозг

получается, что заголовок имеет такую структуру:

typedef struct tagOwenHeader{
WORD Low : 8;
WORD Size : 4;
WORD Query : 1;
WORD High : 3;
}OWEN_HEADER, *POWEN_HEADER;

Low - младший байт адреса
Size - размер поля данных - 2 (2 - это размер хеша имени)
Query - признак запроса
High - старшие 3 бита адреса

перед расчетом CRC хеш команды записывается в пакет перевернутым байтами, т.е. сначала high, потом low,
после расчета CRC, сам CRC так же переворачивается, после чего, вся полученная последовательность кодируется тетрадами

на счет уровня приложения-представления - пока не проверял, будем смотреть дальше

на счет переворачиваний, я пока без самого девайса не понял, но если разбить пакет на 3 сущности: заголовок, команда, CRC, то можно в принципе перевернуть все это дело перед кодированием тетрадами, но тогда не будет сходиться CRC и в данных будет бардак, либо можно тетрады выводить старшим полубайтом вперед, тогда можно не переворачивать, (все зависит от конечной реализации авторов; сам склюняюсь к тому, что все таки напутали порядок полубайтов), вобщем я сделал пока как есть

в приложении мой конфигуратор команд, это консолька, которая позволяет просто собрать команду, в конечный вид, перед отправкой, так что сам девайс ей не нужен (если кто-то будет вводить имя команды, не забывайте про пробелы в конце этой команды, например "dev " или "ver ")

(завтра проверю на девайсе, если успею, а сегодня просто выкладываю, т.к. команды уже сходятся по текущей теме и еще по этой: http://www.owen.ru/forum/showthread.php?t=7784)

mega
09.06.2010, 15:32
а вот и мой исходник этой консольки,
кто желает встроить этот генератор себе в программу:

#include &lt;windows.h&gt;
#include &lt;tchar.h&gt;
#include &lt;locale.h&gt;
#include &lt;stdio.h&gt;
#include &lt;conio.h&gt;
//
BOOL ASCIIToCode( CHAR ch, CHAR*char_for_hash ){
ch = toupper( ch ); //преобразуем все в верхний регистр
if( strchr( "0123456789", ch ) ){
*char_for_hash = ch - '0';
}else if( (ch >= 'A') && (ch <= 'Z') ){
*char_for_hash = ch - 'A' + 10;
}else{
switch( ch ){
case '-':
*char_for_hash = 10 + 26;
break;
case '_':
*char_for_hash = 10 + 26 + 1;
break;
case '/':
*char_for_hash = 10 + 26 + 2;
break;
case ' ':
*char_for_hash = 10 + 26 + 3;
break;
default:
return FALSE;
}
}
*char_for_hash *= 2;
return TRUE;
}
//
USHORT Hash( BYTE Byte, BYTE nbit, USHORT CRC ){
for( INT i = 0; i < nbit; i++, Byte <<= 1){
if( ( Byte ^ ( CRC >> 8 ) ) & 0x80 ){
CRC <<= 1;
CRC ^= 0x8F57;
}else{
CRC <<= 1;
}
}
return CRC;
}
//
typedef union tagOwenHeader{
struct{
WORD Low : 8;
WORD Size : 4;
WORD Query : 1;
WORD High : 3;
};
BYTE Bytes[2];
}OWEN_HEADER, *POWEN_HEADER;
//
int _tmain( int argc, TCHAR *argv[] ) {
INT addr, size, Byte;
TCHAR query[2], cmd_fmt[2];
CHAR a_cmd[100];
BYTE Data[15];
BYTE Command[50];
OWEN_HEADER hdr;
INT l, i;
USHORT hash;
CHAR chfh;
//
_tsetlocale( LC_ALL, TEXT("Russian") );
//
loop:
do{
fflush( stdin );
_tprintf_s( TEXT("Введите адрес устройства (0-2047): ") );
}while( _tscanf_s( TEXT("%i"), &addr ) != 1 );
//
do{
fflush( stdin );
_tprintf_s( TEXT("Это запрос? (y/n): ") );
}while( _tscanf_s( TEXT("%1[yn]"), &query, sizeof(query)/sizeof(TCHAR) ) != 1 );
//
do{
fflush( stdin );
_tprintf_s( TEXT("Введите размер блока данных (2-17): ") );
}while( (_tscanf_s( TEXT("%i"), &size ) != 1) || (size < 2) || (size > 17) );
//
do{
fflush( stdin );
_tprintf_s( TEXT("команда будет введена строкой? (y-строка/n-HASH): ") );
}while( _tscanf_s( TEXT("%1[yn]"), &cmd_fmt, sizeof(cmd_fmt)/sizeof(TCHAR) ) != 1 );
//
switch( cmd_fmt[0] ){
case TEXT('y'):
cmd_loop:
do{
fflush( stdin );
_tprintf_s( TEXT("Введите имя команды (допустимый набор символов: [a-zA-Z0-9-_/ .]): ") );
}while( _tscanf_s( TEXT("%h[a-zA-Z0-9-_/ .]"), &a_cmd, sizeof(a_cmd)/sizeof(CHAR) ) != 1 );
//
_tprintf_s( TEXT("Имя команды: \"%hs\"\r\n"), a_cmd );
//
for( hash = 0, l = strlen( a_cmd ), i = 0 ; l-- ; ++i ){
if( a_cmd[i] != '.' ){
if( !ASCIIToCode( a_cmd[i], &chfh ) ){
goto cmd_loop;
}
if( a_cmd[i + 1] == '.' ){
++chfh;
}
hash = Hash( chfh << 1, 7, hash );
}
}
break;
case TEXT('n'):
do{
fflush( stdin );
_tprintf_s( TEXT("Введите HASH команды: ") );
}while( _tscanf_s( TEXT("%hi"), &hash ) != 1 );
break;
}
_tprintf_s( TEXT("HASH команды: 0x%.4hx\r\n"), hash );
//
for( i = 0, l = size - 2 ; l-- ; ++i ){
do{
fflush( stdin );
_tprintf_s( TEXT("Введите %i-й байт команды (0-255): "), i + 3 );
if( _tscanf_s( TEXT("%i"), &Byte ) != 1 ){
continue;
}
if( Byte < 0 || Byte > 255 ){
continue;
}
Data[i] = Byte;
break;
}while( TRUE );
}
//
hdr.Low = addr;
hdr.High = addr >> 8;
hdr.Query = ( query[0] == TEXT('y') ) ? 1 : 0;
hdr.Size = (BYTE)(size - 2);
Command[0] = hdr.Bytes[0];
Command[1] = hdr.Bytes[1];
Command[2] = (BYTE)(hash >> 8);
Command[3] = (BYTE)hash;
for( i = 0, l = size - 2 ; l-- ; ++i ){
Command[4 + i] = Data[i];
}
//
for( hash = 0, i = 0, l = 2 + size ; l-- ; ++i ){
hash = Hash( Command[i], 8, hash );
}
Command[2 + size] = (BYTE)(hash >> 8);
Command[3 + size] = (BYTE)hash;
//
_tprintf_s( TEXT("--------\r\nКоманда:\r\n") );
for( i = 0, l = 4 + size ; l-- ; ++i ){
_tprintf_s( TEXT("%.2x"), Command[i] );
}
_tprintf_s( TEXT("\r\n#") );
for( i = 0, l = 4 + size ; l-- ; ++i ){
_tprintf_s( TEXT("%c"), 'G' + ( Command[i] >> 4 ) );
_tprintf_s( TEXT("%c"), 'G' + ( Command[i] & 0x0f ) );
}
_tprintf_s( TEXT("\\r\r\n--------\r\n") );
//
do{
fflush( stdin );
_tprintf_s( TEXT("Продолжить? (y/n): ") );
}while( _tscanf_s( TEXT("%1[yn]"), &query, sizeof(query)/sizeof(TCHAR) ) != 1 );
//
if( query[0] == TEXT('y') ){
goto loop;
}
return 0;
}
несколько похожий код я использовал у себя в трансляторе этого протокола в SCPI форму, пока все работает

т.е. для съема-установки параметров использую формат обмена в удобочитаемой форме, типа такого:

#devno:query:command\r

Дмитрий Егоренков
10.06.2010, 12:29
то, что вы языком C плохо владеете, было ясно еще по первому сообщению. совершенно не обязательно выставлять это напоказ.

mega
11.06.2010, 09:21
то, что вы языком C плохо владеете, было ясно еще по первому сообщению. совершенно не обязательно выставлять это напоказ.
интересное умозаключение :)
особенно интересно это слышать от человека, совершенно не понимающего битовую логику (я делаю такой вывод из посыла почитать тот самый документ, очевидные ошибки в котором я уже подчеркнул)

если это техподдержка "овен", сочуствую вашей администрации, т.к. ликбез в культуре общения и программирования тут неизбежен :))

Дмитрий Егоренков
11.06.2010, 12:45
это не умозаключение, а факт. ну ладно, я битовую логику не понимаю, вы-то вообще читать не умеете. посмотрите хотя бы раздел "3.3 структура кадра".

mega
11.06.2010, 15:21
ваши умозаключения неуместны - вот это факт,

я читать умею, и как уже сказал, делал как раз по написанному, а
если вы такой хорошоший специалист, давайте прямо здесь посчитаем пакет по пункту 3.3:

адрес будет 513
запрос будет стандартный - dev:

1. делим адрес на 8 старших и 3 младших бита: 01000000 001b => 0x40 0x01
2. извлекаем старшую часть адреса: 0x40
3. извлекаем младшую часть адреса: 0x01
4. ставим во второй байт бит запроса: 0x09
5. ставим во второй байт размер 0: 0x09
6. ставим хеш имени команды: 0xd681
7. данных нет, контрольную сумму пока пропускаем
8. формируем пакет: 40 09 d6 81

а теперь покажите мне безграмотному, как из этого получить #GHLGTMOHTISS\r, т.е. 01 50 d6 81, грамотный вы наш,
я вам даже предлагаю покрутить этот кубик рубик (попереварачивать байты местами)
почему не получится, да потому что очевидно

во первых берется 8 младших битов,

во вторых - последовательность формирования кадра в описании нарушена, но это еще не все

битовая схема да - иногда она рисуется октетами, тогда порядок бит будет обратный в каждом байте,
только ведь это обязательно надо отмечать либо в тексте, либо направлением либо MSB-LSB прямо в схеме, загляните в любой rfc и посмотрите как там описаны протоколы,

а раз вы не знаете слова "стандарт", то я уже показал вид заголовка на овеновские устройства на си,
причем сделал это не для вас, а для тех, кому непосчасливится вновь читать этот документ

Дмитрий Егоренков
11.06.2010, 19:50
давайте читать вместе, как сказку на ночь.

--
"2.11 Каждое сообщение и квитанция передается старшими байтами вперед."

не полубайтами, не битами, а именно байтами. это относится как ко всему пакету (до кодирования в ASCII), так и к частям пакета -- локальному идентификатору параметра, полю данных и контрольной сумме.

это положение имеет забавное следствие: строки в поле данных лежат старшим байтом вперед, то есть задом наперед.


"структурная схема протокола овен"

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

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


"3.1 Метод передачи байта "Тетрада-в-ASCII-символ"

особо сказать нечего. один байт превращается в два. старший из них опять передается первым (сюрприз!). сказкам про избыточность не верьте -- эффективность такой проверки очень низкая.


"3.3 Структура кадра"

для 11-битных адресов в первом байте пакета находятся _старшие_ восемь бит адреса. младшие три бита находятся в _старших_ битах второго байта пакета.

проще всего забыть про 11-битную адресацию, и считать, что весь адрес помещается в первом байте пакета.

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

блок данных канального уровня -- это дань модели взаимодействия открытых систем, применение которой только запутывает описание протокола. проще думать об этом блоке как о двух сущностях -- локальном идентификаторе (hash-коде) параметра и поле данных.

локальный идентификатор вы худо-бедно посчитали, так что перейдем к CRC.


"4.5 Хеширование имен параметров и вычисление контрольной суммы сообщения"

контрольная сумма пакета -- это классический CRC16 с самопальным полиномом. аккумулятор инициализируется 0. почему в описании дана рекурсивная функция я не знаю -- видимо так она использовалась в доисторических приборах. более вменяемый алгоритм в этой ветке есть.

контрольная сумма помещается в пакет, (вы не поверите!) старшим байтом вперед.
--


истинному знатоку слова стандарт, виртуозу битовой логики и непревзойденному читателю RFC разобраться во всем этом будет несложно.


n'est ce pas?

Дмитрий Егоренков
11.06.2010, 20:07
> а теперь покажите мне безграмотному, как из этого получить #GHLGTMOHTISS\r, т.е. 01 50 d6 81, грамотный вы наш,

короткий ответ: никак.

во-первых, заголовок пакета должен выглядеть так:


typedef union tagOwenHeader
{
struct
{
int High : 8;
int Size : 4;
int Query : 1;
int Low : 3;
};

unsigned char Bytes[2];
}OWEN_HEADER, *POWEN_HEADER;

во-вторых, так как адрес у вас 11-битный, засовывать его в заголовок надо так:


hdr.High = addr >> 3;
hdr.Low = addr & 0x07;

и в результате вы получите то, что и должны:

40 30 D6 81 11 6D и #KGJKTMOHHHMT\В

ну и для всех остальных.

порядок и размер членов битового поля стандартом C не определяется. на другой платформе или компиляторе вы можете получить другие результаты. использование union -- плохая практика, особенно когда записывается один элемент, а читается другой.

и вообще, все это стрельба из пушки по воробьям, потому что проще и понятней писать сразу в буфер (Command в нашем случае).

denver
09.01.2012, 16:59
//Расчет контрольной суммы по алгоритму овен
Function TForm1.owenCRC16(str:string):string;
const
//AsChars: array[0..15] of Char = 'GHIJKLMNOPQRSTUV';
HexChars: array[0..15] of Char = '0123456789ABCDEF';
var
b:byte;i,j:integer; crc:word;
begin
crc:= 0;
for i:= 0 to (length(str)div 2)-1 do
begin
b:=strtoint('$'+copy(str,(2*i)+1,2));
for j:=1 to 8 do
begin
if ((b xor(crc shr 8))and $80)>0 then
begin
crc:=crc shl 1;
crc:=crc xor $8F57;
end
else
crc:=crc shl 1;
b:=b shl 1;
end;
end;
result:=char(HexChars[crc div 4096])+
char(HexChars[(crc mod 4096)div 256])+
char(HexChars[((crc mod 4096)mod 256)div 16])+
char(HexChars[((crc mod 4096)mod 256)mod 16]);
end;

pigeon
26.01.2016, 14:46
Mega, огромное вам спасибо за прогу!!!!!!!!!! Замучился получать эту CRC Для СИ 8. Когда же в России кончится самопал!? И появятся нормальные описания протоколов с примерами.