I'd really like some help in creating a custom function to create an 8-bit polynomial CRC checksum. Is there anyone who can point me in the right direction?

Ian

I'd really like some help in creating a custom function to create an 8-bit polynomial CRC checksum. Is there anyone who can point me in the right direction?

Ian

Hi

I want to process an incoming 15-byte datastream, ie 87 FF FF 7F 7F 7F 7F 00 F8 7F 88 3E 00 FF 08, of which the 15th byte is the checksum. My intention is to loop through each byte calculating the checksum and then comparing the resulting final checksum to the 15th byte. If they're identical the data is OK. I'm going to be recording the data using the Troi Serial plug-in hopefully, but I don't want to buy the plug-in until I know I can calculate the checksum.

Ian

IanWilson wrote:

I want to process an incoming 15-byte datastream, ie 87 FF FF 7F 7F 7F 7F 00 F8 7F 88 3E 00 FF 08, of which the 15th byte is the checksum.

...

I don't want to buy the plug-in until I know I can calculate the checksum.

If the plugin returns a text string in the form of "87 FF FF 7F 7F 7F 7F 00 F8 7F 88 3E 00 FF 08" you

**will**be able to calculate/verify the checksum. And if you can point us to the exact algorithm your provider is using, we may be able to help you with the how.IanWilson wrote:

The algorithm is an 8-bit CRC polynomial x8+x2+x+1. Does this help?

I am afraid it doesn't mean much to me. I did a short look-around, but what I found doesn't seem to fit your situation.

There is another thing you should keep in mind. Most of these algorithms were designed to be executed by hardware operating in real time on binary level. It's possible that the equivalent mathematical operation, when performed in decimal, can be much simpler - perhaps even as simple as suggested by Ralph above. This is well-worth investigating, because Filemaker is by no means a number-cruncher.

Ian

I believe the following 3 custom functions will enable you to achieve what you want:

The first CF converts a hex string to a binary string:

HexAsBinary ( hexString ) =

Let ([

n = Position ( "0123456789ABCDEF" ; Left ( hexString ; 1 ) ; 1 ; 1 ) - 1 ;

b = Choose ( n ; "0000" ; "0001" ; "0010" ; "0011" ; "0100" ; "0101" ; "0110" ; "0111" ; "1000" ; "1001" ; "1010" ; "1011" ; "1100" ; "1101" ; "1110" ; "1111" )

] ;

Case (

IsEmpty ( hexString ) ; "" ; // end recursion

n < 0 ; Evaluate ( "." ) ; // invalid argument

b & HexAsBinary ( Replace ( hexString ; 1 ; 1 ; "" ) )

)

)

The second CF does a bitwise XOR and is called in the final CF:

BitXOR ( bitString1 ; bitString2 ) =

Let ([

b1 = bitString1 ;

b2 = bitString2 ;

l1 = Length ( b1 ) ;

l2 = Length ( b2 )

] ;

Case (

Filter ( b1 ; "01" ) ≠ b1 or Filter ( b2 ; "01" ) ≠ b2 ; Evaluate ( "." ) ; // invalid argument

not ( l1 or l2 ) ; b1 & b2 ; // end recursion

l1 ≠ l2 ; // arguments of unequal length

Left ( b1 ; l1 - l2 ) & Left ( b2 ; l2 - l1 ) & bitXOR ( Replace ( b1 ; l1 > l2 ; l1 - l2 ; "" ) ; Replace ( b2 ; l2 > l1 ; l2 - l1 ; "" ) ) ;

bitXOR ( Left ( b1 ; Length ( b1 ) - 1 ) ; Left ( b2 ; Length ( b2 ) - 1 ) ) & xor ( Right ( b1 ; 1 ) ; Right ( b2 ; 1 ) )

)

)

Finally, CRCcheck takes two binary string arguments (the checksummed string and the divisor) and returns either True (1) or False (0):

CRCcheck ( bitString ; bitDivisor ) =

Let ([

l = Length ( bitDivisor ) ;

bin = Left ( bitString ; l ) ;

rs = Case (

Left ( bin ; 1 ) ≠ "1" ; bitString ;

Replace ( bitString ; 1 ; l ; bitXOR ( bin ; bitDivisor ) )

)

] ;

Case (

Filter ( bitString ; "01" ) ≠ bitString or Filter ( bitDivisor ; "01" ) ≠ bitDivisor ; Evaluate ( "." ) ; // invalid argument

IsEmpty ( bitString ) ; True ; // checksum OK

Left ( rs ; 1 ) = "1" ; False ; // checksum fails

CRCcheck ( Replace ( rs ; 1 ; 1 ;"" ) ; bitDivisor )

)

)

So, the calc you need is:

CRCcheck ( HexAsBinary ( Substitute ( inputString ; " " ; "" ) ) ; "100000111" )

(removing the various tests for invalid arguments will speed things up somewhat)

cheers

Tom

PS It seems that my carefully laid out CFs have been somewhat munged by Jive

Message was edited by: Tom Elliott

Tom: How fantastic ... it just works brilliantly! Many, many thanks. Can I ask one more favour, please? Your functions will verify the incoming string's checksum, however in reply I have to send out a nine-byte reply: eight bytes of data and a checksum byte. Is it possible to modify your functions to take an eight-byte string parameter and return a CRC checksum?

Regards

Ian

- 1 person found this helpful
Ian

It seems to me that the logic for creating a CRC checksum is considerably simpler if, rather than using simply the data string, additional zeros are first added at the end - the number of zeros being the length (in binary) of the required checksum.

The following CF takes two binary string arguments (the data with additional zeros as above and the divisor) and will (I think) return the required checksum byte as a binary string:

CRCcheckSum ( bitString ; bitDivisor ) =

Let ([

l = Length ( bitDivisor ) ;

p1 = Position ( bitString ; "1" ; 1 ; 1 )

] ;

Case (

p1 = 0 or Length ( bitString ) - p1 < l - 1 ; Right ( bitString ; l - 1 ) ; // end recursion

CRCcheckSum ( Replace ( bitString ; p1 ; l ; bitXOR ( Middle ( bitString ; p1 ; l ) ; bitDivisor ) ) ; bitDivisor )

)

)

I assume you want the checksum in hex notation, in which case the following CF may be useful:

BinaryAsHex ( bitString ) =

Let ([

r4 = Right ( "0000" & bitString ; 4 ) ;

n = 8 * Left ( r4 ; 1 ) + 4 * Middle ( r4 ; 2 ; 1 ) + 2 * Middle ( r4 ; 3 ; 1 ) + Right ( r4 ; 1 ) ;

c = Middle ( "0123456789ABCDEF" ; n + 1 ; 1 )

] ;

Case (

Filter ( bitString ; "01" ) ≠ bitString ; Evaluate ( "." ) ; // invalid argument

IsEmpty ( bitString ) ; "" ; // end recursion

BinaryAsHex ( Left ( bitString ; Length ( bitString ) - 4 ) ) & c

)

)

Assuming you are using the same divisor as before, the calc you need for the checksum (in hex) is either:

BinaryAsHex ( CRCcheckSum ( HexAsBinary ( hexDataString & "00" ) ; "100000111" ) )

or

BinaryAsHex ( CRCcheckSum ( HexAsBinary ( hexDataString ) & "00000000" ) ; "100000111" ) )

The latter form is more generally useful, since in general the number of zeros added at the end of the (binary) data should be one less than the length of the (binary) divisor; the following will return the required string of zeros:

Substitute ( Left ( 10 ^ 400 -1 ; Length ( bitDivisor ) - 1 ) ; "9" ; "0" )

===============

BTW I have a couple of improvements to the previous CFs:

Firstly, in BitXOR the calc for dealing with arguments of unequal length is unnecessarily complicated; the line that reads:

Left ( b1 ; l1 - l2 ) & Left ( b2 ; l2 - l1 ) & bitXOR ( Replace ( b1 ; l1 > l2 ; l1 - l2 ; "" ) ; Replace ( b2 ; l2 > l1 ; l2 - l1 ; "" ) ) ;

can be simplified to:

Left ( b1 ; l1 - l2 ) & Left ( b2 ; l2 - l1 ) & bitXOR ( Replace ( b1 ; 1 ; l1 - l2 ; "" ) ; Replace ( b2 ; 1 ; l2 - l1 ; "" ) ) ;

Secondly, I have optimised CRCcheck to skip all leading zeros in one pass (rather than one zero per pass); the new version is:

CRCcheck ( bitString ; bitDivisor ) =

Let ([

l = Length ( bitDivisor ) ;

p1 = Position ( bitString & "1" ; "1" ; 1 ; 1 ) ;

bs = Replace ( bitString ; 1 ; p1 - 1 ; "" ) ;

rs = Replace ( bs ; 1 ; l ; bitXOR ( Left ( bs ; l ) ; bitDivisor ) )

] ;

Case (

Filter ( bitString ; "01" ) ≠ bitString or Filter ( bitDivisor ; "01" ) ≠ bitDivisor ; Evaluate ( "." ) ; // invalid argument

Length ( bs ) < l ; IsEmpty ( bs ) ; // end recursion; OK iff remainder is all zeros

CRCcheck ( Replace ( rs ; 1 ; 1 ; "" ) ; bitDivisor )

)

)

cheers

Tom

- 1 person found this helpful
This is interesting. IMHO, the BitXOR() function could be implemented using a simpler approach:

**BitXOR ( n ; m ) =**Let ( [ LSB = Right ( n ; 1 ) xor Right ( m ; 1 ) ; lenN = Length ( n ) ; lenM = Length ( m ) ] ; Case ( Max ( lenN ; lenM ) > 1 ; BitXOR ( Left ( n ; lenN - 1 ) ; Left ( m ; lenM - 1 ) ) ) & LSB )

Tom: Once again I have to thank-you very, very much.

After making the changes to the previous functions, I noticed there was an error in the line

BinaryAsHex ( CRCcheckSum ( HexAsBinary ( hexDataString ) & "00000000" ) ; "100000111" ) )

(One extra closing bracket) but after correcting it the function worked flawlessly. This is exactly what I need to continue development of my solution.

You're a genius!

Ian

Thank-you for your suggestion. I've replaced Tom's function with yours. The solutions are so much neater than my attempts to convert a C script. I do wish FileMaker would index its repeating fields starting with 0 rather than 1 .... after all they are arrays. Life would be so much simpler when you're trying to convert C and PHP functions into FileMaker!

Ian

IanWilson wrote:

...I do wish FileMaker would index its repeating fields starting with 0 rather than 1 .... after all they are arrays. Life would be so much simpler when you're trying to convert C and PHP functions into FileMaker!

Hi Ian,

For some purposes that would make life easier, but for others not so much. However I believe the overriding consideration for FMI is backward compatibility - and repeating fields go all the way back to when it made little sense for them to be zero-based.

I agree that they are arrays, though, and I'd be happy to see their capabilities further enhanced in that direction.

Regards,

Ray

------------------------------------------------

R J Cologon, Ph.D.

FileMaker Certified Developer

Author, FileMaker Pro 10 Bible

NightWing Enterprises, Melbourne, Australia

http://www.nightwingenterprises.com

------------------------------------------------

Ray Cologon Said “I agree that they are arrays, though, and I'd be happy to see their capabilities further enhanced in that direction. “

I will second that. Every programming language and scripting language I can think of uses arrays at its core and it would be nice to see a more robust implementation in Filemaker.

My 2Cents,

Tim

Timothy R Whisenant

Plastic Fusion Fabricators, Inc.

(256) 852-0378 x. 244

Fax: (256) 852-0388

Ian

I believe the following 3 custom functions will enable you to achieve what you want:

The first CF converts a hex string to a binary string:

HexAsBinary ( hexString ) =

Let ([

n = Position ( "0123456789ABCDEF" ; Left ( hexString ; 1 ) ; 1 ; 1 ) - 1 ;

b = Choose ( n ; "0000" ; "0001" ; "0010" ; "0011" ; "0100" ; "0101" ; "0110" ; "0111" ; "1000" ; "1001" ; "1010" ; "1011" ; "1100" ; "1101" ; "1110" ; "1111" )

] ;

Case (

IsEmpty ( hexString ) ; "" ; // end recursion

n < 0 ; Evaluate ( "." ) ; // invalid argument

b & HexAsBinary ( Replace ( hexString ; 1 ; 1 ; "" ) )

)

)

The second CF does a bitwise XOR and is called in the final CF:

BitXOR ( bitString1 ; bitString2 ) =

Let ([

b1 = bitString1 ;

b2 = bitString2 ;

l1 = Length ( b1 ) ;

l2 = Length ( b2 )

] ;

Case (

Filter ( b1 ; "01" ) ≠ b1 or Filter ( b2 ; "01" ) ≠ b2 ; Evaluate ( "." ) ; // invalid argument

not ( l1 or l2 ) ; b1 & b2 ; // end recursion

l1 ≠ l2 ; // arguments of unequal length

Left ( b1 ; l1 - l2 ) & Left ( b2 ; l2 - l1 ) & bitXOR ( Replace ( b1 ; l1 > l2 ; l1 - l2 ; "" ) ; Replace ( b2 ; l2 > l1 ; l2 - l1 ; "" ) ) ;

bitXOR ( Left ( b1 ; Length ( b1 ) - 1 ) ; Left ( b2 ; Length ( b2 ) - 1 ) ) & xor ( Right ( b1 ; 1 ) ; Right ( b2 ; 1 ) )

)

)

Finally, CRCcheck takes two binary string arguments (the checksummed string and the divisor) and returns either True (1) or False (0):

CRCcheck ( bitString ; bitDivisor ) =

Let ([

l = Length ( bitDivisor ) ;

bin = Left ( bitString ; l ) ;

rs = Case (

Left ( bin ; 1 ) ≠ "1" ; bitString ;

Replace ( bitString ; 1 ; l ; bitXOR ( bin ; bitDivisor ) )

)

] ;

Case (

Filter ( bitString ; "01" ) ≠ bitString or Filter ( bitDivisor ; "01" ) ≠ bitDivisor ; Evaluate ( "." ) ; // invalid argument

IsEmpty ( bitString ) ; True ; // checksum OK

Left ( rs ; 1 ) = "1" ; False ; // checksum fails

CRCcheck ( Replace ( rs ; 1 ; 1 ;"" ) ; bitDivisor )

)

)

So, the calc you need is:

CRCcheck ( HexAsBinary ( Substitute ( inputString ; " " ; "" ) ) ; "100000111" )

(removing the various tests for invalid arguments will speed things up somewhat)

cheers

Tom

PS It seems that my carefully laid out CFs have been somewhat munged by Jive

Message was edited by: Tom Elliott