# AMB Protocol?



## RochesterRC

Does anyone know where I can find any information on the type of data sent across the USB or SERIAL port of a PC from an AMB Decoder box?


Thanks,
Shan


----------



## Tres

The serial is the easy part.
I have them somehwere...(I will see if I can find them)

USB is a pain and AMB won't give them out....

A USB Sniffer could be used.


----------



## RochesterRC

Tres said:


> The serial is the easy part.
> I have them somehwere...(I will see if I can find them)
> 
> USB is a pain and AMB won't give them out....
> 
> A USB Sniffer could be used.


Thank you so much! I figured the serial part wouldn't be to hard.

I imagine it just sends the transponder ID across everytime the person crosses the wire and the software does all the timing calculations.

For the USB part I may just have to make a small app that just listens on the USB port to see what the decoder sends across.

Thanks again!
Shan


----------



## hankster

I looked at the info from the older AMB20 systems. The decoder sends info such as tansponder number, total time, lap time, split times.... a lot of info. When I did timing of the Saturday Night Lightning kart races for ESPN2 they custom made some software to pull the info from the decoder for the live TV feeds and I was able to work with them while they did that. Basically, the software just sorted and formatted the info from the serial port.

This info is saved in the decoder so if your computer would happen to "go down" you can actually keep running the race and recall the info that is stored on the decoder. But I doubt that any aftermarket RC software can do that... it was mainly there for use with events such as kart races.


----------



## RochesterRC

Thanks Hank!

I did not know the decoders actually stored any info or processed the lap times. I thought it was all PC Software driven.


----------



## gh3

Hi, i'm a bit interested in this topic, in particular about the serial protocol, if anyone got any infos please tell me


----------



## hankster

Best bet would be just to open up terminal and connect it to the serial post and view what is coming across the serial port.


----------



## gh3

hankster said:


> Best bet would be just to open up terminal and connect it to the serial post and view what is coming across the serial port.


that's true, but atm i've no decoder, and i'm interested only to know the protocol and the datagram sent to made up a linux timing system


----------



## hankster

It's all sent in ascii text so if you look at the info with a terminal program you can clearly read what is being sent by the decoder.


----------



## coraz

the transmissiom= 9600 8N1
the string = AMB20 Output @010204382050 (Car1;hours2;min4;Sec38;20Mil Sec;50Hits)

If you have some schematics about AMB20 decoder and transmitter or equivalent please sen me 

Best regards 
Coraz


----------



## myosin

coraz said:


> the transmissiom= 9600 8N1
> the string = AMB20 Output @010204382050 (Car1;hours2;min4;Sec38;20Mil Sec;50Hits)
> 
> If you have some schematics about AMB20 decoder and transmitter or equivalent please sen me
> 
> Best regards
> Coraz


I too am trying to find info on the tcp protocol used by the AMB decoder. I can read port 5403 and I am receiving data but it is not ascii - does anyone know how to read this data?


----------



## Tres

Try some of the other options in Wireshark for decodes.
Hex, etc....


----------



## myosin

Tres said:


> Try some of the other options in Wireshark for decodes.
> Hex, etc....


Wireshark is a good "sniffer" but it will not be able to decode the data.

Not to worry though... I already figured it out.

Typical stream:
8E 02 33 00 0F 08 00 00 01 00 01 04 3D 00 00 00 03 04 1D 73 87 00 04 08 E8 7E 98 69 24 50 04 00 05 02 22 00 06 02 03 00 08 02 00 00 81 04 B0 14 02 00 8F

This stream is a "passing" (identified by bytes 9 and 10),
It is passing number 61 
Transponder ID is: 8876xxx
RTC TIME in hex is: E8 7E 98 69
RTC DATE in hex is: 24 50 04 00

Plus a few other types of info.


----------



## gndprx

Does anyone know how the end data is stored in the AMB software? I'd like to be able to pass the data in XML to a web service to store in a database.


----------



## JokerT3F

Hi myosin, I'm trying to emulate the an AMB decoder to connect my lap counter system directly with all software supporting the AMB protocol. 

Do you have any other information about the proctocol :

1. initialization data send by the decoder on port opening
2. Any other data send, not related to the Lap data.

Thank


----------



## myosin

JokerT3F said:


> Hi myosin, I'm trying to emulate the an AMB decoder to connect my lap counter system directly with all software supporting the AMB protocol.
> 
> Do you have any other information about the proctocol :
> 
> 1. initialization data send by the decoder on port opening
> 2. Any other data send, not related to the Lap data.
> 
> Thank


It issues a "status" every 5 seconds - noise level, gps setting, temperature and a few more not-so-important items

other than the lap data and the status every 5 seconds, it doesn't send anything unless a command is sent to it to do things like ask for version number, resend the passings, clear the passings, reset tcp/ip connection, etc.

Unless you know how to read hex, I would recommend using the RS232 port - I haven't checked it because I don't have a serial port but I was told that it is straight ascii text. Fire up any terminal program and you should be able to connect and log everything coming across.


----------



## kzoolou

Joker, did you ever get anywhere with this? I'm kind of interested in how you would go about emulating the hardware decoder?


----------



## francokr

*help*



myosin said:


> Wireshark is a good "sniffer" but it will not be able to decode the data.
> 
> Not to worry though... I already figured it out.
> 
> Typical stream:
> 8E 02 33 00 0F 08 00 00 01 00 01 04 3D 00 00 00 03 04 1D 73 87 00 04 08 E8 7E 98 69 24 50 04 00 05 02 22 00 06 02 03 00 08 02 00 00 81 04 B0 14 02 00 8F
> 
> This stream is a "passing" (identified by bytes 9 and 10),
> It is passing number 61
> Transponder ID is: 8876xxx
> RTC TIME in hex is: E8 7E 98 69
> RTC DATE in hex is: 24 50 04 00
> 
> Plus a few other types of info.


My amb decoder send for example this :
$1E0254A10D002D2249433E00

can you help me to traduce it ?
Thanks


----------



## toybreaker

*lil help here?*

Anyone have what the full translation of the hex that is sent? someone out there must have the translation. This is an exchange of data between the decoder and the PC with the racing app and I believe is AMBrc3

From decoder to desktop
8e:02:33:00:cf:02:00:00:01:00:01:04:b2:9b:01:00:03:04:27:fc:70:00:04:08:e8:19:e6:bd:8a:75:04:00:05:02:33:00:06:02:10:00:08:02:00:00:81:04:fc:05:04:00:8f

desktop to decoder
an ACK

decoder to desktop
8e:02:1f:00:3d:27:00:00:02:00:01:02:1b:00:07:02:21:00:0c:01:7a:06:01:00:81:04:fc:05:04:00:8f

desktop to decoder
an ACK

decoder to desktop
8e:02:1f:00:98:e8:00:00:02:00:01:02:1b:00:07:02:22:00:0c:01:7a:06:01:00:81:04:fc:05:04:00:8f

desktop to decoder
an ACK

Thanks for any help -r


----------



## Pinion King

*Stuck*

Same here, I am trying to translate the Hex but can't get it.


----------



## datrat

i can translate it


----------



## darkus69

*AMB Protocol*

Hi.

I am from Spain.

Sorry for my English 

I have a AMB decoder and I want to buy a soft for it.

But I don't have the protocol to communicate with it.

Somebody have this protocol or info.

Thank's a lot.

Bye.
Darkus


----------



## ambhobby

toybreaker said:


> Anyone have what the full translation of the hex that is sent? someone out there must have the translation. This is an exchange of data between the decoder and the PC with the racing app and I believe is AMBrc3
> 
> From decoder to desktop
> 8e:02:33:00:cf:02:00:00:01:00:01:04:b2:9b:01:00:03:04:27:fc:70:00:04:08:e8:19:e6:bd:8a:75:04:00:05:02:33:00:06:02:10:00:08:02:00:00:81:04:fc:05:04:00:8f
> 
> desktop to decoder
> an ACK
> 
> decoder to desktop
> 8e:02:1f:00:3d:27:00:00:02:00:01:02:1b:00:07:02:21:00:0c:01:7a:06:01:00:81:04:fc:05:04:00:8f
> 
> desktop to decoder
> an ACK
> 
> decoder to desktop
> 8e:02:1f:00:98:e8:00:00:02:00:01:02:1b:00:07:02:22:00:0c:01:7a:06:01:00:81:04:fc:05:04:00:8f
> 
> desktop to decoder
> an ACK
> 
> Thanks for any help -r


I have stared att these dumps for a while and have done a lot of assumptions. I do appreciate all kind of help with calculating the "checksum" or what it is in the beginning of the message, whithout this the decoder seems to reject all commands.

First: the decoder seems to use big endian (i think it's the correct term), the data fields should be read from right to left.

Let's take a look at your example:

8e:02:33:00:cf:02:00:00:01:00:01:04:b2:9b:01:00:03:04:27:fc:70:00:04:08:e8:19:e6:bd:8a:75:04:00:05:02:33:00:06:02:10:00:08:02:00:00:81:04:fc:05:04:00:8f

8e: start of message
02: unknown, a minute ago i thought it was 00 for TO the decoder and 01 FROM the decoder
33:00: the length of the message in hex, 0033=51 bytes* read from L to R
cf:02:00:00 the evil checksum I can't figure out 000002cf=719
01: type of message, this is a passing
00: guess: the data length for the passing is 0 bytes
01: sequence number
04: length 4 bytes
b2:9b:01:00 value of sequence number 00019bb2=105394 (seems to have great uptime?)
03: transponder number
04: length 4 bytes
27:fc:70:00 value of transponder number 0070fc27=7404583
04: lap time
08: length 8 bytes
e8:19:e6:bd:8a:75:04:00 lap time in seconds if you divide by one million, 1255138658.753000 hmm... never seen this huge values on my system. Hope it got natural causes...
05: signal strength
02: lengt 2 bytes
33:00: value of signal strength 0x0033=51
06: hits
02: length 2 bytes
10:00: 0010=16 hits
08: unknown
02: length of this unknown
00:00: value of unknown: zero (doesn't make it easier...)
81: ID of the decoder (the last four bytes of the mac address, guess the first two is the IANA assigned number for AMB.it)
04: length of ID 4 byted
fc:05:04:00: xx:xx:00:04:05:fc
8f: end of message

*If the value is in the range 8A to 8F (could be the range 80-8F i guess) the value is prefixed/escaped by 8D followed by the byte value plus 0x20. The prefix/escape is NOT counted in the length of the message.
Example: signal strength whith the value of 142 0x8E: 05:02:8D:AE:00
Action: subtract AE whith 0x20 => 8E and delete the byte containing 8D.
Result: 05:02:8e:00 

Now: please help me find out how to calculate the "checksum" or what it is.
If someone do I will post all the keys I've found.


----------



## marcmarc3333

Hello !!!

Somebody possesses the coding of date and an hour because the weft it hexa is not clear ?

Excuse for my average English....

Thanks


----------



## xplodwild

@marcmarc3333 : Just look at what ambhobby said. You just have to parse the eight bytes after "00 04" to get the time, then divide it by 1 million to get it in secs (uint64 => divide => double).

Anyway, did someone figured out how to send commands to the decoder ? Does any program use them actually ? If so we could sniff some of these to help finding the checksum calculation

Edit: I've done some searches, it looks just like a simple CRC calculation. Sending this :
8E 00 00 00 5B EB 00 00 24 00 01 00 04 00 05 00 8F

Make the AMB return some result (Race session initialization) :
8E 02 1F 00 00 86 00 00 24 00 01 08 C5 81 79 1A E2 8A 04 00 04 02 00 00 81 04 FA 05 04 00 8F

It looks like C5 81 79 1A E2 8A 04 is the current AMB time (to initialize it for drivers)


Changing the checksum results in "CRC Error" reply from the AMB (in pure ascii text).
Still no clue about what data to check-in for the checksum, but I think we're pretty close.


----------



## credfern

Hi All,

I am trying to do much the same thing. I have tried sending 8E 00 00 00 5B EB 00 00 24 00 01 00 04 00 05 00 8F to my AMBrc3 decoder but it doesn't seem to make a difference. When a passing comes in (either Serial or TCP/IP) i get the same data. Just a transponder number, ticks since the decoder came online, hits and signal strength.

How do i get the decoder to send me the 'verbose' information that you have above i.e. 8E 02 1F 00 00 86 00 00 24 00 01 08 C5 81 79 1A E2 8A 04 00 04 02 00 00 81 04 FA 05 04 00 8F


----------



## mrsponge

*Elusive checksum*

Hi all,

I've also done som reverse engineering on the protocol (of mx3) and reached just about the same point as ambhobby and xplodwild. I created a little program acting as a proxy server relaying the traffic between the software and the decoder while logging it.

However, I have not been able to reverse engineer the checksum... I've used an online crc calculator (not allowed to post link, google it) to test different combinations of the input string, but still no success... Perhaps someone else have better luck...

I'll continue bashing my head aginst those checksums!

Cheers.


----------



## ry81racer

So I am able to see the data in hyper terminal that the decoder is sending after initializing the decoder with another lap counting program. However if I do know do the initializing with the other software first it does not work. Does anyone know what commands I need to send to do the initialization.


----------



## ambhobby

credfern said:


> Hi All,
> 
> I am trying to do much the same thing. I have tried sending 8E 00 00 00 5B EB 00 00 24 00 01 00 04 00 05 00 8F to my AMBrc3 decoder but it doesn't seem to make a difference. When a passing comes in (either Serial or TCP/IP) i get the same data. Just a transponder number, ticks since the decoder came online, hits and signal strength.
> 
> How do i get the decoder to send me the 'verbose' information that you have above i.e. 8E 02 1F 00 00 86 00 00 24 00 01 08 C5 81 79 1A E2 8A 04 00 04 02 00 00 81 04 FA 05 04 00 8F


The decoder is listening on two ports/tcp.
I don't have my notes available but I think it's 5400/tcp and 5900/tcp
The lower port is the "simple" version.

If I remember the wrong numbers: either you can portscan the decoder or connect with Orbits (or what program you is using) and do a "netstat -an|find ESTAB" and find out the port number from the list.


----------



## skier

*messages*

I have read previous posts and seems to me more of you were close to finding the solution.

Does anyone found an answer regarding TCP/IP communication (especially CRC)? 

Don't you have a piece of C++ code able to communicate with RMBRc using TCP/IP (decoding the whole string, sending commands)?

BTW: is there any free software able to use TCP/IP communication?

Thanks


----------



## connywesth

I'm investigating how to kickstart the software Prolap32 from AMB232 interface.

I dont have the AMB232 interface-card but I'm trying to emulate this by sending my own signals to ProLap32 over serialport.

We have an old AMB8800 with upgrade to AMB20 transponders, and an very old ISA8 PC-card. The problem is that this ISA8 card inly works with Windws95c as tha latest operating system and a very old computer that supports the ISA8-port PC-BUS.

To buy a AMB232-card is just to expensive.

We are investigating a solution where we just convert the physical signals from the AMB20 interface cable that normally connexts to the ISA8-card but we convert this through a Paralell-to-USB converter and build a software (we are 2 software developers by profession that do this, but we have less hardware and AMB20-ISA8 skills).

We have already the software in our development environment set upp but I have some problems to know exactly what protocol-signals to send to the ProLap software (when interfacing over AMB232). 

We receive CtsHolding, DsrHolding and CDHolding from ProLap32 but what should I send back to get ProLap32 start kicking?

Our objective is to make our software AMB232 Compatible by a combination of hardware and software interface. This whould make it possible to do without the old Win95 computer and we can build som softwatre to make more information availible, but still using marketleading software as competition system.

We have the TimeRecord-format and the LapCount-format, but I think it is some problem when ProLap32 starts and it expects some pin to be set to start the timing software.

Any Ideas how to solve this in the com-port communication?

Regards,
Conny


----------



## jaemiem

have you had any luck with the software development? I have an old 8800 system with amb20 transponders aswell. Trying to bypass the ISA card for a more portable solution. Its either that or upgrade or try and find an old AMB20 Decoder with serial.

Anything to get usb to parallel or a PCMCIA or powered parallel port to work with alycat or even Freelaps...

any help would be appreciated...


----------



## connywesth

No, I'm still working on the problem....

Will put more time into this because it's interesting to learn about this.


----------



## connywesth

ambhobby said:


> I have stared att these dumps for a while and have done a lot of assumptions. I do appreciate all kind of help with calculating the "checksum" or what it is in the beginning of the message, whithout this the decoder seems to reject all commands.
> 
> First: the decoder seems to use big endian (i think it's the correct term), the data fields should be read from right to left.
> 
> Let's take a look at your example:
> 
> 8e:02:33:00:cf:02:00:00:01:00:01:04:b2:9b:01:00:03:04:27:fc:70:00:04:08:e8:19:e6:bd:8a:75:04:00:05:02:33:00:06:02:10:00:08:02:00:00:81:04:fc:05:04:00:8f
> 
> 8e: start of message
> 02: unknown, a minute ago i thought it was 00 for TO the decoder and 01 FROM the decoder
> 33:00: the length of the message in hex, 0033=51 bytes* read from L to R
> cf:02:00:00 the evil checksum I can't figure out 000002cf=719
> 01: type of message, this is a passing
> 00: guess: the data length for the passing is 0 bytes
> 01: sequence number
> 04: length 4 bytes
> b2:9b:01:00 value of sequence number 00019bb2=105394 (seems to have great uptime?)
> 03: transponder number
> 04: length 4 bytes
> 27:fc:70:00 value of transponder number 0070fc27=7404583
> 04: lap time
> 08: length 8 bytes
> e8:19:e6:bd:8a:75:04:00 lap time in seconds if you divide by one million, 1255138658.753000 hmm... never seen this huge values on my system. Hope it got natural causes...
> 05: signal strength
> 02: lengt 2 bytes
> 33:00: value of signal strength 0x0033=51
> 06: hits
> 02: length 2 bytes
> 10:00: 0010=16 hits
> 08: unknown
> 02: length of this unknown
> 00:00: value of unknown: zero (doesn't make it easier...)
> 81: ID of the decoder (the last four bytes of the mac address, guess the first two is the IANA assigned number for AMB.it)
> 04: length of ID 4 byted
> fc:05:04:00: xx:xx:00:04:05:fc
> 8f: end of message
> 
> *If the value is in the range 8A to 8F (could be the range 80-8F i guess) the value is prefixed/escaped by 8D followed by the byte value plus 0x20. The prefix/escape is NOT counted in the length of the message.
> Example: signal strength whith the value of 142 0x8E: 05:02:8D:AE:00
> Action: subtract AE whith 0x20 => 8E and delete the byte containing 8D.
> Result: 05:02:8e:00
> 
> Now: please help me find out how to calculate the "checksum" or what it is.
> If someone do I will post all the keys I've found.


Our club bought a AMBrc3 system early this year and we are thrilled about it. Very nice to have, and very nice to race with proper timing system.

We are also investigating the AMB P3 Protocol and how to make the most use of it. Since we have access to several members that are system developers by profession (my self included).

I read your article and we have also tested with TCP-listeners like the Wireshark-software (thanx for the tip).

I guess that what you think is lap time actually is the total elapsed time from when the decoder was started. When we made some calculation of the time we just subtracted the time from the previous lap and we got quite reasonable times (about 5.38 seconds). I tested by first subtracting 0xFFFFFFF000000000 (7 Fs and 9 Zeroes) to lower the value in the calculation.

I also guess the second byte 0x02 after the 0x8e can be the section code for the packet header.

As you also noticed it seems each section onsists of a section code of 1 byte directly followed by a length specifier. I think this is also valid for the packet header maybe with the exception that it is 2 bytes for the packet length specifier.

I wonder if byte 4 value 0x00 actually is the section code of CRC-checksum section?

I doubt that byte# 10 is a length, as its value is 0x00, this seems odd?

I think the sequence number is a unique lap identification number? I have also read the RMonitor Timing Protocol (dated October 1, 2001 from Track Timing Software Solutions) and it seems to have some similarities with the AMB P3-protocol.

I found a very useful page about Cyclic Redundancy Check (CRC)-checksums at Wkipedia (http://en.wikipedia.org/wiki/Cyclic_redundancy_check) and some sample source-code at codeproject.com (http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=5597). We also have to concider what bytes are included when calculating the CRC-checksum. I have asumed that all bytes after the checksum itself are included, and i have made some calculations by reversing the order of all bytes... I have to try more....

It seems to be a lot of different variants of calculating the CRC-Checksum. I'm investigating more about this.

I also guess that the section with code 0x08 at the end of the "Passing" packet can be either temperature or GPS coordinates of the decoder, byt I'm just guessing. Maybe someone have a GPS or temperature installed and have the possibility to run Wireshark to investigate this?

I think it is a pitty that MyLaps doesn't publish the P3 protocol. It seems so old fashioned these days. Soon enough we will succeed in reverse engineer this information anyway in our open community, this just a matter of time by the hobbyists.


----------



## CreativeIndy

connywesth, If I can help in anyway let me know. 

I am a Sr. software engineer and although C#/.Net is my daily I am heavily skilled in C++ and proficient in VB. So if you need an extra hand with something let me know. 

I had thoughts about buying a used system to do a breakdown to see if I could come up with an alternate solution to break the monopoly they have on this hobby!


----------



## connywesth

CreativeIndy,

The most important task is probably to analyse the AMB P3-protocol (P3 for short) and what output information from the Decoder, but almost equally important is to analyse what CRC routine is used for calculating the CRC-checksum in the P3. This is important to send comands to the Decoder. The Decoder rejects all comands with wrong CRC record.

The messages that is sent from and to the Decoder is build up as bytearrays with certain record structure, we have in this discussion threat, analysed parts of the "passing message", when a RC Car passes over the antenna loop.

At this moment I work with C++ to learn how to set upp a TCPSocket to do the same work as I did in C# some weeks ago.

It is easy to set upp a TCPListener-class (depreciated) or TCPClient-class i C#, VB.NET and any orther managed .NET code, is is easy to set up a Decoder simulator (by using the TCPServer-class) with the only purpose to send out randomly generated messages for testing the TCPListener oc TCPClient. The tricky part is to have the protocol specification and working to get any meaningful work from the setup.

I just build an TCPClient application that reads the IPAddress from the Decoder and outputs the binary values (represented as two-char Hex-values like xFF for decimal 255 and xA0 for decimal 160) in my local network.

What you need to set this up for testing:

- AMB/MyLaps Decoder rc3 or rc4
- Loop cables and LoopBox
- 1 personal transponder (I use MRT PTX) with battery powersupply (like when it is installed in a RC Car)
- A portable or stationary Computer
- A simple network Switch or Router
- 2 network-cables to connect the computer and the decoder to the Switch/Router
- Some powersupplys to get it all to work
- Some software to listen to the TCP-Ports like WireShark (availible for both Windows and Linux)
- And a really sharp mind... ;-)

I'm not an expert developer in the protocol area so any help in analysing this is apreciated.

Thanx for asking....


----------



## connywesth

*Problems with calculating CRC....*

I have done some research with the CRC, and i dont think it is compliant to the CRC16-CCITT standard.

This article http://www.codeproject.com/Articles/35134/How-to-calculate-CRC-in-C?msg=4283673#xx4283673xx at codeproject.com i downloaded some sample code to carlculate CRC16-CCITT and CRC32-CCITT but none of them works for calculating the AMBrc messages. Im convinced it is a 16-bit CRC algorithm but dont know wich, there is so many different algorithms.

I added som test-code like this:



Code:


// C#-code for Microsoft .NET Framework
//
// Original bytearray is bytes13a below.
//
// Byte 0 is always startbyte 0x8e
// Byte 1-3 is headerinfo
// Byte 4-7 is checksum
// Byte 8-11 is body data
// Byte 12 is message stop code 0x8f
// I think (but is certanly not sure) that bytes13b or bytes13e is the correct to calculate checksum for
// But I dont get it right, it can possibly be using another crc agorithm. The message is part of a X400
// package transported by a TCP/IP package.
// I belive it is a 16 bit checksum but its placed in a 32-bit "slot" in the protocol.
byte[] bytes13a = new byte[13] { 0x8e, 0x00, 0x00, 0x00, 0x74, 0xb8, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x8f };
byte[] bytes13b = new byte[13-8] { 0x24, 0x00, 0x01, 0x00, 0x8f };
byte[] bytes13c = new byte[13-4] { 0x8e, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x8f };
byte[] bytes13d = new byte[13 - 7] { 0x00, 0x00, 0x24, 0x00, 0x01, 0x00 };
byte[] bytes13e = new byte[13 - 9] { 0x24, 0x00, 0x01, 0x00};
 
Console.WriteLine("CRC-16 expected CRC [74b80000] for bytearray is {0:x4}", getCRC16(bytes13b));
 

public static UInt16 getCRC16(byte[] bytes)
{
	CRC16 crc16 = new CRC16();
	byte[] newBytes = new byte[bytes.Length];
	for (int i=0; i<bytes.Length; i++) newBytes[i]=bytes[i];
	if (BitConverter.IsLittleEndian)
	{
		Array.Reverse(newBytes);
	}
	return BitConverter.ToUInt16(crc16.ComputeHash(newBytes), 0);
}

This message is an initial message that the decoder sends to the RCM software and that "opens" the RCM software for recieving data from the decoder.

The message appears twice that is 13 bytes foloowed by another 13 bytes message in the same TCP/IP package.

I strongly believe that the array bytes13b (or maybe bytes13e) is the right one to calculate the CRC-value for. If we can break that 5 byte mystery, we are home free on this issue.

In this message the CRC-value is 74 b8 00 00 that is in reverse order 47220 (0xb874), and the array is { 0x24, 0x00, 0x01, 0x00, 0x8f }, how will that happen? Has anyone any good contact with any highshool teachers?


----------



## connywesth

*The entire c#-code included....*



Code:


//////////////////////////////////////////////////////////////////////////////////
// This is file: Program.cs, it is the main startingpoint for the testproject.
//////////////////////////////////////////////////////////////////////////////////
// Author: Conny Westh, 2012
// E-Mail: [email protected]
//////////////////////////////////////////////////////////////////////////////////
// To compile, you only need to include the file CRC16.cs in the project from
// http://www.codeproject.com/Articles/35134/How-to-calculate-CRC-in-C?msg=4283673#xx4283673xx
//////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace crctest
{
	class Program
	{
		static void Main(string[] args)
		{
			//testCRC16();
			testRigg();
			Console.WriteLine("Press [ENTER] to continue...");
			Console.ReadLine();

		}


		public static void testRigg()
		{
			//byte[] bytes = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
			//byte[] bytes1 = new byte[10] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
			byte[] bytes13a = new byte[13] { 0x8e, 0x00, 0x00, 0x00, 0x74, 0xb8, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x8f };
			byte[] bytes13b = new byte[13-8] { 0x24, 0x00, 0x01, 0x00, 0x8f };
			byte[] bytes13c = new byte[13-4] { 0x8e, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x8f };
			byte[] bytes13d = new byte[13 - 7] { 0x00, 0x00, 0x24, 0x00, 0x01, 0x00 };
			byte[] bytes13e = new byte[13 - 9] { 0x24, 0x00, 0x01, 0x00};


			byte[] bytes13ar = new byte[bytes13a.Length];
			byte[] bytes13br = new byte[bytes13b.Length];
			byte[] bytes13cr = new byte[bytes13c.Length];
			byte[] bytes13dr = new byte[bytes13d.Length];
			byte[] bytes13er = new byte[bytes13b.Length];

			for (byte i = 0; i < bytes13a.Length; i++)
			{
				bytes13ar[bytes13a.Length - i - 1] = bytes13a[i];
			}

			for (byte i = 0; i < bytes13b.Length; i++)
			{
				bytes13br[bytes13b.Length - i - 1] = bytes13b[i];
			}

			for (byte i = 0; i < bytes13c.Length; i++)
			{
				bytes13cr[bytes13c.Length - i - 1] = bytes13c[i];
			}

			for (byte i = 0; i < bytes13d.Length; i++)
			{
				bytes13dr[bytes13d.Length - i - 1] = bytes13d[i];
			}

			for (byte i = 0; i < bytes13e.Length; i++)
			{
				bytes13er[bytes13e.Length - i - 1] = bytes13e[i];
			}



			try
			{

				Console.WriteLine("");
				Console.WriteLine("Test of transponder message 13 characters!");
				test("74b80000", "13a", bytes13a);
				test("74b80000", "13b", bytes13b);
				test("74b80000", "13c", bytes13c);
				test("74b80000", "13d", bytes13d);
				test("74b80000", "13e", bytes13e);

				Console.WriteLine("");
				Console.WriteLine("Test of transponder message 13 characters, reverse order!");
				test("74b80000", "13ar", bytes13ar);
				test("74b80000", "13br", bytes13br);
				test("74b80000", "13cr", bytes13cr);
				test("74b80000", "13dr", bytes13dr);
				test("74b80000", "13er", bytes13er);

			}
			catch
			{ }
		}

		public static void test(string expectedCRC, string arrayName, byte[] bytes)
		{
			Console.WriteLine("CRC-16 ({0}) for bytearray {1} is {2:x4}", expectedCRC, arrayName, getCRC16(bytes));
			//Console.WriteLine("Sum-16 ({0}) for bytearray {1} is {2:x4}", expectedCRC, arrayName, getSum16(bytes));
			//Console.WriteLine("CRC-32 ({0}) for bytearray {1} is {2:x8}", expectedCRC, arrayName, getCRC32(bytes));
			//Console.WriteLine("Sum-32 ({0}) for bytearray {1} is {2:x8}", expectedCRC, arrayName, getSum32(bytes));

		}


		public static UInt16 getCRC16(byte[] bytes)
		{
			CRC16 crc16 = new CRC16();
			byte[] newBytes = new byte[bytes.Length];
			byte[] retArr = new byte[2];
			for (int i=0; i<bytes.Length; i++) newBytes[i]=bytes[i];
			if (BitConverter.IsLittleEndian)
			{
				Array.Reverse(newBytes);
			}
			retArr=crc16.ComputeHash(newBytes);
			Console.Write("[{0:x2}{1:x2}]", retArr[0], retArr[1]);
			return BitConverter.ToUInt16(retArr,0);
		}
	}
}



Code for the CRC16.cs file:


Code:


// Tamir Khason http://khason.net/
//
// Released under MS-PL : 6-Apr-09

using System;
using System.Collections;
using System.IO;
using System.Security.Cryptography;
using System.Text;

   /// <summary>Implements a 16-bits cyclic redundancy check (CRC) hash algorithm.</summary>
   /// <remarks>This class is not intended to be used for security purposes. For security applications use MD5, SHA1, SHA256, SHA384, 
   /// or SHA512 in the System.Security.Cryptography namespace.</remarks>
   public class CRC16 : HashAlgorithm {

      #region CONSTRUCTORS
      /// <summary>Creates a CRC16 object using the <see cref="DefaultPolynomial"/>.</summary>
      public CRC16() : this(DefaultPolynomial) {
      }

      /// <summary>Creates a CRC16 object using the specified polynomial.</summary>
      [CLSCompliant(false)]
      public CRC16(ushort polynomial) {
         HashSizeValue = 16;
         _crc16Table = (ushort[])_crc16TablesCache[polynomial];
         if (_crc16Table == null) {
            _crc16Table = CRC16._buildCRC16Table(polynomial);
            _crc16TablesCache.Add(polynomial, _crc16Table);
         }
         Initialize();
      }

      // static constructor
      static CRC16() {
         _crc16TablesCache = Hashtable.Synchronized(new Hashtable());
         _defaultCRC = new CRC16();
      }
      #endregion

      #region PROPERTIES
      /// <summary>Gets the default polynomial.</summary>
      [CLSCompliant(false)]
      public static readonly ushort DefaultPolynomial = 0x8408; // Bit reversion of 0xA001;
      #endregion

      #region METHODS
      /// <summary>Initializes an implementation of HashAlgorithm.</summary>
      public override void Initialize() {
         _crc = 0;
      }

      /// <summary>Routes data written to the object into the hash algorithm for computing the hash.</summary>
      protected override void HashCore(byte[] buffer, int offset, int count) {
         for (int i = 0; i < buffer.Length; i++) {
            byte index = (byte)(_crc ^ buffer[i]);
            _crc = (ushort)((_crc >> 8) ^ _crc16Table[index]);
         }
      }

      /// <summary>Finalizes the hash computation after the last data is processed by the cryptographic stream object.</summary>
      protected override byte[] HashFinal() {
         byte[] finalHash = new byte[2];
         ushort finalCRC = (ushort)(_crc ^ _allOnes);

         finalHash[0] = (byte)((finalCRC >> 0) & 0xFF);
         finalHash[1] = (byte)((finalCRC >> 8) & 0xFF);

         return finalHash;
      }

      /// <summary>Computes the CRC16 value for the given ASCII string using the <see cref="DefaultPolynomial"/>.</summary>
      public static short Compute(string asciiString) {
         _defaultCRC.Initialize();
         return ToInt16(_defaultCRC.ComputeHash(asciiString));
      }

      /// <summary>Computes the CRC16 value for the given input stream using the <see cref="DefaultPolynomial"/>.</summary>
      public static short Compute(Stream inputStream) {
         _defaultCRC.Initialize();
         return ToInt16(_defaultCRC.ComputeHash(inputStream));
      }

      /// <summary>Computes the CRC16 value for the input data using the <see cref="DefaultPolynomial"/>.</summary>
      public static short Compute(byte[] buffer) {
         _defaultCRC.Initialize();
         return ToInt16(_defaultCRC.ComputeHash(buffer));
      }

      /// <summary>Computes the hash value for the input data using the <see cref="DefaultPolynomial"/>.</summary>
      public static short Compute(byte[] buffer, int offset, int count) {
         _defaultCRC.Initialize();
         return ToInt16(_defaultCRC.ComputeHash(buffer, offset, count));
      }

      /// <summary>Computes the hash value for the given ASCII string.</summary>
      /// <remarks>The computation preserves the internal state between the calls, so it can be used for computation of a stream data.</remarks>
      public byte[] ComputeHash(string asciiString) {
         byte[] rawBytes = ASCIIEncoding.ASCII.GetBytes(asciiString);
         return ComputeHash(rawBytes);
      }

      /// <summary>Computes the hash value for the given input stream.</summary>
      /// <remarks>The computation preserves the internal state between the calls, so it can be used for computation of a stream data.</remarks>
      new public byte[] ComputeHash(Stream inputStream) {
         byte[] buffer = new byte[4096];
         int bytesRead;
         while ((bytesRead = inputStream.Read(buffer, 0, 4096)) > 0) {
            HashCore(buffer, 0, bytesRead);
         }
         return HashFinal();
      }

      /// <summary>Computes the hash value for the input data.</summary>
      /// <remarks>The computation preserves the internal state between the calls, so it can be used for computation of a stream data.</remarks>
      new public byte[] ComputeHash(byte[] buffer) {
         return ComputeHash(buffer, 0, buffer.Length);
      }

      /// <summary>Computes the hash value for the input data.</summary>
      /// <remarks>The computation preserves the internal state between the calls, so it can be used for computation of a stream data.</remarks>
      new public byte[] ComputeHash(byte[] buffer, int offset, int count) {
         HashCore(buffer, offset, count);
         return HashFinal();
      }
      #endregion

      #region PRIVATE SECTION
      private static ushort _allOnes = 0xffff;
      private static CRC16 _defaultCRC;
      private static Hashtable _crc16TablesCache;
      private ushort[] _crc16Table;
      private ushort _crc;

      // Builds a crc16 table given a polynomial
      private static ushort[] _buildCRC16Table(ushort polynomial) {
         // 256 values representing ASCII character codes. 
         ushort[] table = new ushort[256];
         for (ushort i = 0; i < table.Length; i++) {
            ushort value = 0;
            ushort temp = i;
            for (byte j = 0; j < 8; j++) {
               if (((value ^ temp) & 0x0001) != 0) {
                  value = (ushort)((value >> 1) ^ polynomial);
               } else {
                  value >>= 1;
               }
               temp >>= 1;
            }
            table[i] = value;
         }
         return table;
      }

      private static short ToInt16(byte[] buffer) {
         return BitConverter.ToInt16(buffer, 0);
      }
      #endregion

   }


----------



## skier

Hi all,
Do you have any success in decoding AMBrc TCP/IP? Are you able to write a console application which can read the ambrc4 and write decoded data into console window (e.g. C#)?
I'm trying to do the same with the support of your previous articles, but it's still hard work . Can you help?


----------



## connywesth

*Simple AMBListener C#-code*

Here is a very simple AMBListener as a C# Console Application:



Code:


////////////////////////////////////////////////////////////////////
// File: AMBListener.cs
////////////////////////////////////////////////////////////////////
// AMBListener is a simple class in C# for connect and listening 
// to MyLaps/AMB RC3 and RC4 Decoder output over TCPIP.
////////////////////////////////////////////////////////////////////
// Author: Conny Westh, Sweden, 2011, [email protected]
////////////////////////////////////////////////////////////////////
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;

namespace AMBListener
{
	class AMBListener
	{
		public static void Connect(String server, Int32 port, String message)
		{
			Console.WriteLine("nStarting AMBListener...");
			while (true)
			{
				try
				{
					// Create a TcpClient.
					// Note, for this client to work you need to have a TcpServer 
					// connected to the same address as specified by the server, port
					// combination.
					TcpClient client = new TcpClient(server, port);

					// Translate the passed message into ASCII and store it as a Byte array.
					Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);

					// Get a client stream for reading and writing.
					//  Stream stream = client.GetStream();

					NetworkStream stream = client.GetStream();
					stream.ReadTimeout = 10000;
					stream.WriteTimeout = 10000;


					// Send the message to the connected TcpServer. 
					stream.Write(data, 0, data.Length);

					// Receive the TcpServer.response.
					while (client.Connected)
					{
						// Buffer to store the response bytes.
						data = new Byte[256];

						// String to store the response ASCII representation.
						String responseData = String.Empty;

						// Read the first batch of the TcpServer response bytes.
						Int32 bytes = stream.Read(data, 0, data.Length);
						responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);

						// Write hexdump to console for debugging.....
						string str = "";
						for (int index = 0; index < bytes; index++)
						{
							str += String.Format("{0:x2} ", data[index]);
						}
						Console.WriteLine(str);
					}

					// Close everything.
					client.Close();
				}
				catch (ArgumentNullException e)
				{
					Console.WriteLine(String.Format("ArgumentNullException: {0}", e));
				}
				catch (SocketException e)
				{
					// This Exception has to be active but content commented out, otherwise it will be many, many errormessages.....
					Console.WriteLine(String.Format("SocketException: {0}", e));
				}
				catch (IOException e)
				{
					// This Exception has to be active but content commented out, otherwise it will be many, many errormessages.....
					Console.WriteLine(String.Format("IOException: {0}", e));
				}
				catch (Exception e)
				{
					Console.WriteLine(String.Format("Exception: {0}", e));
				}

				//Console.WriteLine(String.Format("n Restarting AMBListener..."));
				//Console.WriteLine(String.Format("n Press Enter to continue..."));
				//Console.Read();
			}
		}

	}
}




Code:


////////////////////////////////////////////////////////////////////
// File: Program.cs
////////////////////////////////////////////////////////////////////
// When this sample Windows Console Application code in C# is run
// the AMBListener will start a Console Windows 
// and start displaying continues messages from AMB Decoder
////////////////////////////////////////////////////////////////////
// Author: Conny Westh, Sweden, 2011, [email protected]
////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AMBListener
{
	class Program
	{
		static void Main(string[] args)
		{
			// You will probably need to adjust IPAddress and portnumber for this to work
			string IPAddressToDecoder = "192.168.0.1";
			int IPPortForListeingToDecoder = 5403;
			string MessageToDecoder = "This is a a Hello message sent to AMB Decoder!";
			AMBListener.Connect(IPAddressToDecoder, IPPortForListeingToDecoder, MessageToDecoder);
		}
	}
}


----------



## connywesth

skier said:


> Hi all,
> Do you have any success in decoding AMBrc TCP/IP? Are you able to write a console application which can read the ambrc4 and write decoded data into console window (e.g. C#)?
> I'm trying to do the same with the support of your previous articles, but it's still hard work . Can you help?


If you crack down the CRC algoritm or solve the entire protocol issue you will be todays hero...


----------



## vlad_vy

You can find many useful info about MYLAPS (AMB) P3 protocol at this project:
https://github.com/skoky/ambmylapstiming/tree/master/src/com/skoky/P3tools

For calculate CRC you need first parse message for delete ESCs. Then you need replace CRC bytes with 0. Next you can calculate CRC.
https://github.com/skoky/ambmylapstiming/blob/master/src/com/skoky/P3tools/data/tools/CRC16.java



> *If the value is in the range 8A to 8F (could be the range 80-8F i guess) the value is prefixed/escaped by 8D followed by the byte value plus 0x20. The prefix/escape is NOT counted in the length of the message.


If the value is 0x8d, 0x8e or 0x8f and it's not the first or last byte of the message, the value is prefixed/escaped by 0x8D followed by the byte value plus 0x20.


----------



## vlad_vy

ESC = 0x8d // Escape character
ESCx = 0x20 // value to subtract from escaped char
SOR = 0x8e // Start of Record
EOR = 0x8f // End of Record
FOR (Field of Record) = [TOF(Type of Field, 1 byte), Length(1 byte), Data(variable length)]

DEFAULT_VERSION=0x02


Header of any record (from the decoder or to the decoder):
===========================
00 - SOR (Start of Record = 8e)
01 - Version (default = 02)
02 - length of record LSB
03 - length of record MSB
04 - CRC of record LSB
05 - CRC of record MSB
06 - Flags of record LSB
07 - Flags of record MSB
08 - TOR (Type of Record) LSB
09 - TOR (Type of Record) MSB
===========================
10 - FORs (Fields of Record)
...
...
EOF (End of Record = 8f)


TOR (Type of Record, 2 byte)
===========================
RESET = 0x00
PASSING = 0x01
STATUS = 0x02
FIRST_CONTACT = 0x45
ERROR = 0xFFFF
VERSION = 0x03
RESEND = 0x04
CLEAR_PASSING = 0x05
WATCHDOG = 0x18
PING = 0x20
SIGNALS = 0x2d
SERVER_SETTINGS = 0x13
SESSION = 0x15
GENERAL_SETTINGS = 0x28
LOOP_TRIGGER = 0x2f
GPS_INFO = 0x30
TIMELINE = 0x4a
GET_TIME = 0x24
NETWORK_SETTINGS=0x16
// TIMMING_SETTING = 0x12
===========================


----------



## vlad_vy

Record 
8e:02:33:00:cf:02:00:00:01:00:01:04:b2:9b:01:00:03:04:27:fc:70:00:04:08:e8:19:e6:bd:8a:75:04:00:05:02:33:00:06:02:10:00:08:02:00:00:81:04:fc:05:04:00:8f

Header
8e: - SOR (Start of Record)
02: - Version
33:00: - length of record = 00 33 = 51
cf:02: - CRC = 02 cf
00:00: - Flags = 00 00
01:00: - TOR (Type of Record) = 00 01

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

TOR (Type of Record) - PASSING = 0x1

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

Body of the record
01:04:b2:9b:01:00:03:04:27:fc:70:00:04:08:e8:19:e6:bd:8a:75:04:00:05:02:33:00:06:02:10:00:08:02:00:00:81:04:fc:05:04:00:8f

Details (Fields of Record "PASSING"):
PASSING_NUMBER = 0x01 (4 byte) (example of "FOR" - 0x01(TOF):0x04(legth):0x00:0x00:0x00:0x00)
TRANSPONDER = 0x03 (4 bytes)
RTC_ID = 0x13 (4 bytes)
RTC_TIME = 0x04 (8 byte, time microseconds after January 1, 1970 00:00:00 GMT)
UTC_TIME = 0x10 (8 byte, time microseconds after January 1, 1970 00:00:00 GMT)
STRENGTH = 0x05 (2 byte)
HITS = 0x06 (2 byte)
FLAGS = 0x08 (2 byte)
TRAN_CODE = 0x0a (1 byte)
USER_FLAG = 0x0e (4 byte)
DRIVER_ID = 0x0f (1 byte)
SPORT = 0x14 (1 byte)
VOLTAGE = 0x30 (1 byte, V = (float)voltage/10)
TEMPERATURE = 0x31 (1 byte, T = temperature - 100)

General details (Fields of Record):
DECODER_ID = 0x81 (4 byte)
CONTROLLER_ID = 0x83 (4 byte)
REQUEST_ID = 0x85 (8 byte)

With offset = 10 from start of record
==========================
01: - PASSING_NUMBER
04: - length 4 bytes
b2:9b:01:00: - 00 01 9b b2
03: - TRANSPONDER
04: - length 4 bytes
27:fc:70:00: - 00 70 fc 27 (7404583)
04: - RTC_TIME
08: - length 8 bytes
e8:19:e6:bd:8a:75:04:00: - 00 04 75 8a bd e6 19 e8 = Sat Oct 10 01:37:38 UTC+0000 2009
05: - STRENGTH
02: - length 2 bytes
33:00: - 00 33 = 51
06: - HITS
02: - length 2 bytes
10:00: - 00 10 = 16
08: - FLAGS
02: - length 2 bytes
00:00: - 0000
81: - DECODER_ID
04: - length 4 bytes
fc:05:04:00: - 00 04 05 fc
8f - EOR (End of Record)

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


----------



## vlad_vy

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

TOR (Type of Record) "VERSION" = 0x03

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

Command
[SOR(0x8e), DEFAULT_VERSION(0x02), 0x00, 0x00, 0x00(CRC), 0x00(CRC), 0x00, 0x00, TOR(0x03), 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0A, 0x00, 0x0C, 0x00, EOR(0x8f)]

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

Returned record 
Details (Fields of Record "VERSION"):
DECODER_TYPE = 0x01 (1 byte)
DESCRIPTION = 0x02 (variable)
VERSION = 0x03 (variable)
RELEASE = 0x04 (4 byte)
REGISTRATION = 0x08 (8 byte)
BUILD_NUMBER = 0x0A (2 byte)
OPTIONS = 0x0C (4 byte)

General details (Fields of Record):
DECODER_ID = 0x81 (4 byte)
CONTROLLER_ID = 0x83 (4 byte)
REQUEST_ID = 0x85 (8 byte)

DECODER_TYPE:
0x10 = "AMBrc"
0x11 = "AMBMX"
0x12 = "TranX" 
0x13 = "TranX Pro"
0x14 = "TranX Pro"

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


----------



## vlad_vy

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

TOR (Type of Record) "RESET" = 00

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

Command
[SOR(0x8e), DEFAULT_VERSION(0x02), 0x00, 0x00, 0x00(CRC), 0x00(CRC), 0x00, 0x00, TOR(0x00), 0x00, EOR(0x8f)]

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


----------



## vlad_vy

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

TOR (Type of Record) "CLEAR_PASSING" = 0x05

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

Command
[SOR(0x8e), DEFAULT_VERSION(0x02), 0x00, 0x00, 0x00(CRC), 0x00(CRC), 0x00, 0x00, TOR(0x05), 0x00, EOR(0x8f)]

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

Returned record
General details (Fields of Record):
DECODER_ID = 0x81 (4 byte)
CONTROLLER_ID = 0x83 (4 byte)
REQUEST_ID = 0x85 (8 byte)
=====================================


----------



## vlad_vy

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

TOR (Type of Record) "ERROR" = 0xFFFF;

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

Details (Fields of Record "ERROR"):
CODE = 0x1 (2 byte)
DESCRIPTION = 0x02 (variable)

General details (Fields of Record):
DECODER_ID = 0x81 (4 byte)
CONTROLLER_ID = 0x83 (4 byte)
REQUEST_ID = 0x85 (8 byte)

Error codes:
0x0001 CRC Error
0x0002 SOR Missing
0x0003 Header corrupt
0x0004 TOR Unknown
0x0005 Parameters missing
0x0006 Length of record to long
=====================================


----------



## vlad_vy

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

TOR (Type of Record) "STATUS" = 0x02

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

Details (Fields of Record "STATUS"):
NOISE = 0x01 (2 byte)
GPS = 0x06 (1 byte)
(0=false, 1 =true)
TEMPERATURE = 0x07 (2 byte)
SATINUSE = 0x0a (1 byte)
LOOP_TRIGGERS = 0x0b (1 byte)
INPUT_VOLTAGE = 0x0c (1 byte)
(Voltage = (float) input_voltage/10)

General details (Fields of Record):
DECODER_ID = 0x81 (4 byte)
CONTROLLER_ID = 0x83 (4 byte)
REQUEST_ID = 0x85 (8 byte)
=====================================


----------



## vlad_vy

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

TOR (Type of Record) "RESEND" = 0x04

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

Details (Fields of Record "RESEND"):
FROM = 0x01 (4 byte)
UNTIL = 0x02 (4 byte)

General details (Fields of Record):
DECODER_ID = 0x81 (4 byte)
CONTROLLER_ID = 0x83 (4 byte)
REQUEST_ID = 0x85 (8 byte)
=====================================


----------



## lawrencereddin

Try some of the other options in Wireshark for decodes.
Hex, etc....

=============================================
Laser Pointers


----------



## jasistemas

My software with vb.net
strModified = "8E-02-33-00-46-2F-00-00-01-00-01-04-92-01-00-00-03-04-A4-5C-3C-00-04-08-80-61-D9-8C-F1-CB-04-00-05-02-6F-00-06-02-55-00-08-02-00-00-81-04-29-07-04-00-8F"

(I have changed : by "-")


THIS NEXT IS TO ELIMINATE the esc character as vlady says:
Dim arrParejas As Array = strModified.ToUpper.Split("-")


Dim longitudMensaje As Decimal = 0 ' Longitud del mensaje (28 o 51)

'Dim registroTranspondedor As Boolean = False ' Indica si el tipo de registro es de chip

'Dim j As Integer
'---------------------
' Ciclo PRIMARIO PARA LEER TODA LA CADENA.. quiza convenga hacerlo con a patin sin ciclo
'---------------------
' For j = 0 To arrParejas.Length - 1

' Inicio del mensaje
'----------------------------
' Valores que se iran CARGANDO para luego grabarlos en la base de datos
'----------------------------
Dim consecutivo As Long = 0
Dim transpondedor As String = ""
Dim fechaChip As String = ""
Dim LAPTIME As String = ""
Dim fuerza As Integer = 0
Dim hits As Integer = 0


Dim continuarProceso As Boolean = False
If arrParejas(0) = "8E" Then
continuarProceso = True
End If


If continuarProceso Then
'***************
' Locating ESC characters to take next pair and take -20 in a new array
'*************** 
Dim cadenaHEXADECIMAL As String = ""
Dim arrParejasNUEVO As New ArrayList

For i As Integer = 0 To arrParejas.Length - 1
If i = 0 Or i = arrParejas.Length - 1 Then
' el iniico y fin se pasan como van
arrParejasNUEVO.Add(arrParejas(i))

Else
If arrParejas(i) = "8D" Or arrParejas(i) = "8E" Or arrParejas(i) = "8F" Then
Dim anterior As String = arrParejas(i + 1) ' elemento a la derecha
Dim Number1, Number2, Result As Long

Number1 = Convert.ToInt64(anterior, 16)
Number2 = 32 ' 0X20 If the value is 0x8d, 0x8e or 0x8f and it's not the first or last byte of the message, the value is prefixed/escaped by 0x8D followed by the byte value plus 0x20
Result = Number1 - Number2 ' le restamos el 20 para obtener el valor de byte???
' borramos la carga del elemento anterior

' 2013.. el elemento NO SE AGREGA a la nueva cadena
arrParejasNUEVO.Add(Convert.ToString(Result, 16).ToUpper)
i = i + 1 ' nos brincaremos 1 elemento
Else
arrParejasNUEVO.Add(arrParejas(i))
End If

End If
Next

arrParejas = arrParejasNUEVO.ToArray

End If


----------



## connywesth

*New Social Group on HobbyTalk regarding AMB/MyLaps*

I created a open (free to join and it is not moderated) social group for AMB / MyLaps Timing and Lap Counting System, please join.

http://www.hobbytalk.com/bbs1/group.php?groupid=13


----------



## Sjoske

*Some success, some notes*

Hi,

I managed to verify the CRC16 procedure, ported to python, of skoky's java code.
CRC is computed on packet including 8E and 8F bytes, with CRC bytes set to 00, as mentioned by vlad_vy.
That works fine on wireshark collected packets from an AMB unit.
Have seen packets of type STATUS/PASSING/GET_TIME/TIMELINE/NETWORK_SETTINGS.
So the CRC problem seems solved.
I only interpret STATUS and PASSING packets now. Need to code more...

I also tried that using captured packets with the code connywesth's code, but I think that packet collection has some 0d/0a (CR/LF) bytes in there, in my case for PASSING packets, breaking the CRC. STATUS packets were fine though. Other packets I didn't capture.
Example: 8e:02:3d:00:50:d3:00:00:01:00:01:04:42:6d:04:00:0d:0a:08:50:56:2d:33:38:34:36:33:04:08:80:95:f0:e8:3a:d1:04:00:05:02:7a:00:06:02:28:00:08:02:00:00:11:01:03:14:01:03:81:04:08:0f:02:00:8f
^^ ^^ Maybe it's only 0d ...

I'm using the python twisted framework to collect packets. Sorry, no C#




vlad_vy said:


> Record
> 8e:02:33:00:cf:02:00:00:01:00:01:04:b2:9b:01:00:03:04:27:fc:70:00:04:08:e8:19:e6:bd:8a:75:04:00:05:02:33:00:06:02:10:00:08:02:00:00:81:04:fc:05:04:00:8f
> 
> Header
> 8e: - SOR (Start of Record)
> 02: - Version
> 33:00: - length of record = 00 33 = 51
> cf:02: - CRC = 02 cf
> 00:00: - Flags = 00 00
> 01:00: - TOR (Type of Record) = 00 01
> 
> =============================
> 
> TOR (Type of Record) - PASSING = 0x1
> 
> =============================
> 
> Body of the record
> 01:04:b2:9b:01:00:03:04:27:fc:70:00:04:08:e8:19:e6:bd:8a:75:04:00:05:02:33:00:06:02:10:00:08:02:00:00:81:04:fc:05:04:00:8f
> 
> Details (Fields of Record "PASSING"):
> PASSING_NUMBER = 0x01 (4 byte) (example of "FOR" - 0x01(TOF):0x04(legth):0x00:0x00:0x00:0x00)
> TRANSPONDER = 0x03 (4 bytes)
> RTC_ID = 0x13 (4 bytes)
> RTC_TIME = 0x04 (8 byte, time microseconds after January 1, 1970 00:00:00 GMT)
> UTC_TIME = 0x10 (8 byte, time microseconds after January 1, 1970 00:00:00 GMT)
> STRENGTH = 0x05 (2 byte)
> HITS = 0x06 (2 byte)
> FLAGS = 0x08 (2 byte)
> TRAN_CODE = 0x0a (1 byte)
> USER_FLAG = 0x0e (4 byte)
> DRIVER_ID = 0x0f (1 byte)
> SPORT = 0x14 (1 byte)
> VOLTAGE = 0x30 (1 byte, V = (float)voltage/10)
> TEMPERATURE = 0x31 (1 byte, T = temperature - 100)
> 
> General details (Fields of Record):
> DECODER_ID = 0x81 (4 byte)
> CONTROLLER_ID = 0x83 (4 byte)
> REQUEST_ID = 0x85 (8 byte)
> 
> With offset = 10 from start of record
> ==========================
> 01: - PASSING_NUMBER
> 04: - length 4 bytes
> b2:9b:01:00: - 00 01 9b b2
> 03: - TRANSPONDER
> 04: - length 4 bytes
> 27:fc:70:00: - 00 70 fc 27 (7404583)
> 04: - RTC_TIME
> 08: - length 8 bytes
> e8:19:e6:bd:8a:75:04:00: - 00 04 75 8a bd e6 19 e8 = Sat Oct 10 01:37:38 UTC+0000 2009
> 05: - STRENGTH
> 02: - length 2 bytes
> 33:00: - 00 33 = 51
> 06: - HITS
> 02: - length 2 bytes
> 10:00: - 00 10 = 16
> 08: - FLAGS
> 02: - length 2 bytes
> 00:00: - 0000
> 81: - DECODER_ID
> 04: - length 4 bytes
> fc:05:04:00: - 00 04 05 fc
> 8f - EOR (End of Record)
> 
> ==========================


----------



## Berrymartin

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

TOR (Type of Record) "CLEAR_PASSING" = 0x05

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

Command
[SOR(0x8e), DEFAULT_VERSION(0x02), 0x00, 0x00, 0x00(CRC), 0x00(CRC), 0x00, 0x00, TOR(0x05), 0x00, EOR(0x8f)]

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

Returned record
General details (Fields of Record):
DECODER_ID = 0x81 (4 byte)
CONTROLLER_ID = 0x83 (4 byte)
REQUEST_ID = 0x85 (8 byte)
=====================================


----------



## gnulnulf

Is the serial protocol known?


Code:


<stringsign>B1022XX7910100623E29A600
#B16A
#B167
<stringsign>B10202XX4D01007C33089000

I see the transponder ID's but I cannot manage to get the time .
01007C33089 is the changing part but I cannot see a normal difference of about 33 seconds between them.


----------



## Sjoske

I cannot interpret your code.
In the UDP/TCP protocol the time is given as an 8-byte number, least significant byte first. It is a long integer indicating the number of picoseconds since 1-1-1970 (the Epoch...). I take the 8 byte number, divide it by 1e6 (to get a double), and convert it with ctime. 
Maybe this helps?


----------



## gnulnulf

Serial data from Tranx-2

PASSING: $B102AAF79100FEA52E33AE00
B1 - LoopID
02 - Version?
AAF791 - transponder
00FEA52E - time in msec
33 - hits?
AE - strength?
00 - always 00 (EOR)?

[ SOR('$'), LOOPID(2HEX),VERSION(2HEX), TRANSPONDER(6HEX), TIME(8HEX), HITS(2HEX), STRENGTH(2HEX), EOR('00') + CRLF ]

I don't know this for sure, but it seems to fit the captured data

ALIVE: #B166
B1 - Loop ID
66 - strength 
[ SOR('#'), LOOPID(2HEX), STRENGTH(2HEX) + CRLF ]


----------



## Sjoske

Normally there should be a Strength indicator and a Hits count in a Passing.
From a manual I read that:
- Strength should be >60 above noise level, normally well above >100
- Hits is normally higher than 20, typically around 40
In UDP packets there are more fields, like flags (unknown to me), sport, voltage, temperature, to name a few.
Time in 4 bytes... doesn't ring a bell.
Transponder I see as 32-bit int, or 8-byte string



gnulnulf said:


> Serial data from Tranx-2
> 
> PASSING:
> B1 - always B1
> 02 - Version?
> AAF791 - transponder
> 00FEA52E - time
> 33AE - unknown
> 00 - always 00
> 
> ALIVE: #B166
> B1 - always B1
> 66 - strength
> 
> any ideas on the unknown?


----------



## psafth

Hi guys! (and gurls, if any)

I'm currently developing a lapcounter software based on this AMB P3 Protocol.

In general, everything seems to work just wonderful except in some rare occasions.

This is a "passing" message from the decoder.

8e 02 33 00
eb 1d 00 00
01 00
01 04 9d 09 00 00
03 04 e4 d2 36 00
04 08 10 79 8d af e4 f2 ce 04 00
05 02 5f 00
06 02 2e 00
08 02 00 00
81 04 be 13 04 00
8f

Pay attension to 04 08 10 79 8d af e4 f2 ce 04 00. It's not 8 bytes, it's 9 bytes. My guess is that a CRC check would not validate this message (haven't implemented that yet, but it's on it's way.).

So, when this message gets invalidated by the CRC check how can I get the decoder to resend the message? Has anyone of you seen a command for that?

/ psafth


----------



## connywesth

psafth said:


> Hi guys! (and gurls, if any)
> 
> I'm currently developing a lapcounter software based on this AMB P3 Protocol.
> 
> In general, everything seems to work just wonderful except in some rare occasions.
> 
> This is a "passing" message from the decoder.
> 
> 8e 02 33 00
> eb 1d 00 00
> 01 00
> 01 04 9d 09 00 00
> 03 04 e4 d2 36 00
> 04 08 10 79 8d af e4 f2 ce 04 00
> 05 02 5f 00
> 06 02 2e 00
> 08 02 00 00
> 81 04 be 13 04 00
> 8f
> 
> Pay attension to 04 08 10 79 8d af e4 f2 ce 04 00. It's not 8 bytes, it's 9 bytes. My guess is that a CRC check would not validate this message (haven't implemented that yet, but it's on it's way.).
> 
> So, when this message gets invalidated by the CRC check how can I get the decoder to resend the message? Has anyone of you seen a command for that?
> 
> / psafth


Before yoy run the CRC check you need to "deEscape" the bytearray. Se entry #42: http://www.hobbytalk.com/bbs1/showpost.php?p=4400460&postcount=42 where vlad_vy wrote about how to do this. It seems that the 0x8d ESC-char and the following char that needs to be tranformed. And you also need t but 0 in the Place where you have the checksum itself Before running the CRC check.


----------



## psafth

connywesth said:


> Before yoy run the CRC check you need to "deEscape" the bytearray. Se entry #42: where vlad_vy wrote about how to do this. It seems that the 0x8d ESC-char and the following char that needs to be tranformed. And you also need t but 0 in the Place where you have the checksum itself Before running the CRC check.



Yes, that's true. My bad. Still, if the crc wouldn't validate. Is there a command to get a RESEND?

/ psafth


----------



## Lolok33

Hi,
File CRC16 is not present in GitHub.
Can someone publish this code ?
I tried this CRC16 algorithm but it doesn't works : h..p://introcs.cs.princeton.edu/java/51data/CRC16.java.html



vlad_vy said:


> You can find many useful info about MYLAPS (AMB) P3 protocol at this project:
> -------
> 
> For calculate CRC you need first parse message for delete ESCs. Then you need replace CRC bytes with 0. Next you can calculate CRC.
> ----
> 
> 
> If the value is 0x8d, 0x8e or 0x8f and it's not the first or last byte of the message, the value is prefixed/escaped by 0x8D followed by the byte value plus 0x20.


----------



## barsk

vlad_vy said:


> You can find many useful info about MYLAPS (AMB) P3 protocol at this project:
> github.com/skoky/ambmylapstiming/tree/master/src/com/skoky/P3tools
> 
> For calculate CRC you need first parse message for delete ESCs. Then you need replace CRC bytes with 0. Next you can calculate CRC.
> github.com/skoky/ambmylapstiming/blob/master/src/com/skoky/P3tools/data/tools/CRC16.java
> 
> If the value is 0x8d, 0x8e or 0x8f and it's not the first or last byte of the message, the value is prefixed/escaped by 0x8D followed by the byte value plus 0x20.


Hi, I am trying to decode P3 protocol messages in Java and stumbles on the crc checksum. I have three different implementations of the CRC16 checksum and they all give different results, none of them equal to the CRC checksum from the P3 header itself.

Skoky's original code that Vlad refers to here is not available. Any hints on this would be much appreciated. 

I have done what is required, de-escaped (not necessary on the STATUS message as I can see so no 0x8d bytes there), and then put zeroes in the CRC fields in the message, then calculate crc on the whole message from and including start and end. No luck...


----------



## aminear

*stumbled across this from unknown source*

I came across this info in a AMBrc protocol document, it is formatted poorly because of my conversion from pdf document to text.


Appendix B - CRC16 calculation 

The following ‘C’ code is taken from the decoder source so represents exactly the way the CRC is implemented. 

// initialize the CRC16 table 

extern void InitCRC16( void ) { 
inti,j; 
WORD crc ; 

for( i=0; i<256 ;i+=1){ 
for(crc =i<<8,j=0 ;j<8 ;j+=1) 
crc= (crc <<1) ^((crc&0x8000 )?0x1021: 0); 
CRC16Table_= crc; 
} 
} 

// calculate the CRC of a string pointed at by p 
extern WORD CalcCRC16( char*p) { 
WORD CRC ; 

for ( CRC = 0xFFFF ; *p != 0 ; p++ ) // for all chars of a string 
CRC=CRC16Table[ ((CRC >> 8 )&255 )]^( CRC<<8) ^*p ; 
return CRC ; 
} 

// …assuming a preceding InitCRC16() has initialized the CRC16 table… 
// build a full record, ‘Message’ is command without <SOR>, <CRC> and //<EOR> 
sprintf( buf, "%c%sx%X\n", 1, Message, CalcCRC16( Message ) ) ; 
// buf is now command with <SOR>, <CRC> and <EOR>_


----------



## connywesth

Code:


// http://www.hobbytalk.com/bbs1/12-general-rc-discussion/73738-amb-protocol-5.html
// Converted to C# from the original C-kode published by aminear in 
// November 21, 2015, 06:59 PM in post #65 in the above link.
// I have not tested this code, but It seems aproximatly right, 
// 
// The source code translator from C to C#had some problem with translation of 
/ sbyte and byte that could not be resolved automatically. 
// Compiles with no error or warnings in Visual Studio S2017 Community Edition...
using System;

namespace CRC16Library
{
    // Appendix B - CRC16 calculation 
    // The following ‘C’ code is taken from the decoder source so represents exactly the way the CRC is implemented. 
    public class CRC16Libary
    {
        private static ushort[] CRC16Table = new ushort[256];
        // initialize the CRC16 table 

        public CRC16Libary()
        {
            InitCRC16();
        }

        //========================================================================
        // This conversion was produced by the Free Edition of
        // C++ to C# Converter courtesy of Tangible Software Solutions.
        // Order the Premium Edition at https://www.tangiblesoftwaresolutions.com
        //========================================================================

        // initialize the CRC16 table 

        public static void InitCRC16()
        {
            int i, j;
            ushort crc;

            for (i = 0; i < 256; i += 1)
            {
                crc = (ushort)(i << 8);
                for (j = 0; j < 8; j += 1)
                {
                    crc = (ushort)((crc << 1) ^ (((crc & 0x8000) != 0) ? 0x1021 : 0));
                }
                CRC16Table[i] = crc;
            }
        }

        // calculate the CRC of a string pointed at by p 
        //C++ TO C# CONVERTER TODO TASK: Pointer arithmetic is detected on the parameter 'p', so pointers on this parameter are left unchanged:
        public static ushort CalcCRC16(byte[] p)
        {
            ushort CRC;
            CRC = 0xFFFF;
            for (int ptr=0; ptr<p.Length; ptr++) // for all chars of a string
            {
                CRC = (ushort)(CRC16Table[((CRC >> 8) & 255)] ^ (CRC << 8) ^ p[ptr]);
            }
            return CRC;
        }

    }
}


This is how to use the class:



Code:


using CRC16Library;
using System;

namespace CRC16
{
    class Program
    {
        const byte SOR = 0x8e; // Start Of Message
        const byte EOR = 0x8f; // End Of Mesage
        const byte ESC = 0x8d; // Escape char

        static void Main(string[] args)
        {
            // <SOM> = 0x8e
            // <EOM> = 0x8f
            // <ESC> = 0x8d, decrease the following char by 32 (decimal) or 0x20
            // When you create the escaped message you need tp parse through 
            // the entire message and when you find any of the values 0x8d. 
            // 0x8e, 0x8f you have to replace them with a 2 byte replacement like:
            // 
            // To ESCAPE
            // 0x8d => 0x8d, 0xad 
            // 0x8e => 0x8d, 0xae 
            // 0x8f => 0x8d, 0xaf 
            // 
            // To UNESCAPE
            // 0x8d, 0xad => 0x8d
            // 0x8d, 0xae => 0x8e
            // 0x8d, 0xaf => 0x8f

            // Escaped message
            // 8e 02 33 00
            // eb 1d 00 00
            // 01 00
            // 01 04 9d 09 00 00
            // 03 04 e4 d2 36 00
            // 04 08 10 79 8d af e4 f2 ce 04 00
            // 05 02 5f 00
            // 06 02 2e 00
            // 08 02 00 00
            // 81 04 be 13 04 00
            // 8f

            // Unescaped message, AND ZERO out the CRC positions in row 2....
            // 02 33 00
            // 00 00 00 00
            // 01 00
            // 01 04 9d 09 00 00
            // 03 04 e4 d2 36 00
            // 04 08 10 79 8f e4 f2 ce 04 00
            // 05 02 5f 00
            // 06 02 2e 00
            // 08 02 00 00
            // 81 04 be 13 04 00


            List<int> lineBreak;
            byte[] message;

            // FIRST TEST
            lineBreak = new List<int>() { 3, 7, 9, 15, 21, 31, 35, 39, 43, 49 };
            message = new byte[]
            {
                  0x02, 0x33, 0x00
                , 0x00, 0x00, 0x00, 0x00 // This is the CRC values in the leftmost 2 positions
                , 0x01, 0x00
                , 0x01, 0x04, 0x9d, 0x09, 0x00, 0x00
                , 0x03, 0x04, 0xe4, 0xd2, 0x36, 0x00
                , 0x04, 0x08, 0x10, 0x79, 0x8f, 0xe4, 0xf2, 0xce, 0x04, 0x00
                , 0x05, 0x02, 0x5f, 0x00
                , 0x06, 0x02, 0x2e, 0x00
                , 0x08, 0x02, 0x00, 0x00
                , 0x81, 0x04, 0xbe, 0x13, 0x04, 0x00
            };
            TestCRC("First Test", message, lineBreak);

            // SECOND TEST
            lineBreak = new List<int>() {1, 4, 8, 10, 16, 22, 32, 36, 40, 44, 50 };
            message = new byte[]
            {
                SOR
                , 0x02, 0x33, 0x00
                , 0x00, 0x00, 0x00, 0x00 // This is the CRC values in the leftmost 2 positions
                , 0x01, 0x00
                , 0x01, 0x04, 0x9d, 0x09, 0x00, 0x00
                , 0x03, 0x04, 0xe4, 0xd2, 0x36, 0x00
                , 0x04, 0x08, 0x10, 0x79, 0x8f, 0xe4, 0xf2, 0xce, 0x04, 0x00
                , 0x05, 0x02, 0x5f, 0x00
                , 0x06, 0x02, 0x2e, 0x00
                , 0x08, 0x02, 0x00, 0x00
                , 0x81, 0x04, 0xbe, 0x13, 0x04, 0x00
                , EOR
            };
            TestCRC("SECOND TEST (WITH SOM & EOM)", message, lineBreak);

            // …assuming a preceding InitCRC16() has initialized the CRC16 table… 
            // build a full record, ‘Message’ is command without <SOR>, <CRC> and //<EOR> 
            // sprintf(buf, "%c%sx%X\n", 1, Message, CalcCRC16(Message));
            // buf is now command with <SOR>, <CRC> and <EOR>
        }


        private static void TestCRC(string TestHeader, byte[] message, List<int> lineBreak)
        {
            Console.WriteLine("");
            Console.WriteLine($"-----   {TestHeader}   -----");
            Console.WriteLine("");

            //CRC16Libary crc16 = new CRC16Libary();
            ushort crc = CRC16Libary.CalcCRC16(message);
            int count = 0;

            Console.Write("Message=[");
            foreach (byte b in message)
            {
                if (count > 0)
                {
                    Console.Write($", ");
                }
                Console.Write($"0x{b:X2}");
                count++;
                if (lineBreak.IndexOf(count) >= 0)
                {
                    Console.WriteLine();
                }

            }

            Console.WriteLine("];");
            ushort right = (ushort)((crc % 256));
            ushort left = (ushort)((crc - right) / 256);
            Console.WriteLine();
            Console.WriteLine("ACTUAL RESULT:");
            Console.Write("Big Endian: ");
            Console.WriteLine($"CRC16:{crc}, Leftvalue:0x{left:X2}, RightValue:0x{right:X2} ");

            right = (ushort)((crc % 256) / 256);
            left = (ushort)(crc - right);
            Console.Write("Little Endian: ");
            Console.WriteLine($"CRC16:{crc}, Leftvalue:0x{left:X2}, RightValue:0x{right:X2} ");

            Console.WriteLine();
            Console.WriteLine("EXPECTED RESULT:");
            Console.Write("Big Endian: ");
            Console.WriteLine($"CRC16:{0xeb * 256 + 0x1d}, Leftvalue:0x{0xeb:X2}, RightValue:0x{0x1d:X2} ");

            Console.Write("Little Endian: ");
            Console.WriteLine($"CRC16:{0xeb + 0x1d * 256}, Leftvalue:0x{0xeb:X2}, RightValue:0x{0x1d:X2} ");

        }

        //public static implicit operator sbyte(byte b)
        //{
        //    sbyte sb = unchecked((sbyte)b);
        //    return sb;
        //}

        //public static implicit operator byte(sbyte sb)
        //{
        //    byte retVal = (byte)sb;
        //    return retVal;
        //}
    }
}


I have made a misstake when converting the C code to C#, so i can clearly see that the CRC16 value is not correct. Mabe someone can help debuging where I made the misstake?

I get this output in my console after running the test code:



This i NOT right, I did something wrong in my CRC16-Library, but maybe someone can test and dubbelcheck what I missed. I hate C pointer arithmatic....

Regards,
Conny Westh


----------



## connywesth

I found a thread at RcTech https://www.rctech.net/forum/radio-...ourglass-diy-lap-timing-aka-cano-revised.html where a couple of forum members has build an actual a Lap Counting System with hardware, Transponder, Loop Interface Box, and Decoder.

The system is named: "RCHourglass" by Howard Cano. (https://github.com/mv4wd/RCHourglass/wiki)

The system is not completley compatible with AMB/MyLaps system, but its a start, to work from. The project is OpenSource.

The main hardware Developer is Howard Cano, and he deserves all Credit for his enormous work in this project. Several other forum members has build clones and fine tuned some components of the system. They renamed the Loop Interface Box to Loop Amplifier, but Transponder and Decoder is named the same.

The carrier frequency from the Transponder is 5 MHz, and it is only 1/4 of the Square Wave that is datacarrier. That is the datafrequency is 1,25 MHz.

This project has not, at this moment, coded the P3/P4 protocolls from the Decoder to the PC Software.


Some links in the subject:
Lap_Timing_Decoder by Howard Cano | Photobucket


----------



## Youri




----------

