HAC4 Data Format

A description of the raw data format is available in CVS in the directory tourviewer/src/doc .

      
    
    INTRODUCTION

Observations of the CCHAC4 and CM414M (only parts) data format.


NOTES

This is all information I know about the data format of the
CCHAC4. All this information was extracted with the help of a hex
editor and close observation. This description is incomplete and at
worst incorrect. Use it at your own risk!

There are a few thing I can't quite understand. As a matter of fact
storing all numbers as ASCII charactes wastes half of the space. This
way each byte (8-bit)  only stores a 4-bit value. Furthermore it seems
a bit overblown to use 20% of the file for stop bytes (the checksum
would probably do the job). IMHO transfers could be speed up so
battery usage could be significantly reduced if the data was stored
more packed.

If you take a base converter and a calculator you might find that some
of your computed values will vary to those displayed by software. This
is because common software programms may aproximate values. In example
the temperature is only saved every 2 minutes. You wouldn't normally
expect temperature to change rapidlyso this is fine, but if it does
software programms might compute intermediate values.

Important contribution about missing fields in the file header and
where the offsets are stored and computed where made by Dirk
F. Raetzel.

An implementation of software that can evaluates CCHAC4 raw data can
be downloaded at http://tourviewer.sf.net/.

If you have further questions or want to contribute to this document,
feel free to contact me. 

Steffen Pingel 


PHYSIKAL PROPERTIES

9600 bits per second,
8 data bits,
no parity,
1 stop bit.


GENERAL

Each file has a size of 81930 bytes and starts with four letters
"AFRO". Every fifth byte is used as a stop byte, it has the value 0Dh
(ASCII carriage return). Actually every single byte is stored as an
ASCII character which then has to be converted to numbers. 

Depending on the type of data, some values are binary-coded-decimals
(BCD) and others are hex-decimals. In example the number 20 would be
stored as 32h 30h where the 2 is represendted by 32h (50) which is the
ASCII value of "2" and 0 is represented by 30h (48) which is
equivalent to "0". The letters comtained in hex-decimals can be either
stored lower-case or upper-case. In example C0h could be either
represented by 43h 30h or 66h 30h. 

The last five bytes of each file make up the checksum. The checksum is
calculated by adding up all 16384 4-byte hex-decimals and masking
them with FFFFh. See http://tourviewer.sf.net/contrib/ for an example
implementation. 

The EEPROM is initialized by a test procedure to the value 5555h.


DATA

Subtracting the signature and the checksum leaves 81920 bytes of
data. This data is divided into records a 40 byte. These 40 bytes
contain 8 4-byte numbers (the other 8 bytes are used for stop
bytes). This means the unit can store 2048 records. 

Each file starts with 16 records (right after the signature, offset 5)
which I don't have figured out, yet. Most of these records contain
5555h anyway.

A few words about the syntax of the following paragraphs. The first
column contains the absolute decimal file offset or the device
type. The second column contains a symbolic description of the record
(???? = don't know, . = stop byte).

The symbols are explained below the record description (t = text, d =
decimal, h = hex-decimal, o = offset).

The offsets used in the data files differ from those used in this
text. The data file offsets are hex-decimals that count the number of
data words up to the offset, ignoring the stop bytes and
signature. They can be converted to absolute decimal file offsets by
multiplication with 2.5 and addition of 5.


FILEHEADER

---------------------------------------------------------------------------
0	ssss.

	ssss	t	"AFRO" signature (AFRO-Fluginstrumente GmbH)
---------------------------------------------------------------------------
5+	hhmm.sstt.hhmm.sstt.hhmm.sstt.hhmm.sstt.

	hh	d	? intermediate stop watch hour
	mm	d	? intermediate stop watch minute
	ss	d	? intermediate stop watch second
	tt	h	? intermediate stop watch second / 100
			multiply by 100/128 to get original value
---------------------------------------------------------------------------
645

HAC4	cccc.pppp.wwww.aaaa.bbbb.cccc.dddd.eeee.

	cccc	h	"B735" (HAC4)
				"B7B4" (HAC4-Imp)
				other values (HAC4-325)
	pppp	h	wheel perimeter (mm)
	wwww	h	weight (kg)
	aaaa	h	home altitude (m)
			"FFFF"	not set
	bbbb	h	1. pulse upper bound (bpm)
	cccc	h	1. pulse lower bound (bpm)
	dddd	h	2. pulse upper bound (bpm)
	eeee	h	2. pulse lower bound (bpm)

CM414M	cccc.pppp.qqqq.????.aaaa.wwww.mmdd.yyyy.

	cccc	h	"B723" (CM414M)
	pppp	h	? 1. wheel perimeter (mm)
	qqqq	h	? 2. wheel perimeter (mm)
	????	h	?
	aaaa	h	home altitude (m)
			"FFFF"	not set
	wwww	h	weight (kg)
	mm	d	month of transfer
	dd	d	day of transfer
	yyyy	d	year of transfer
---------------------------------------------------------------------------
685

HAC4	aabb.ccdd.eeee.llll.kkkk.oooo.yyyy.mmdd

	aa	d	1. count down minutes
	bb	d	1. count down seconds
	cc	d	2. count down minutes
	dd	d	2. count down seconds
	eeee	h	altitude error correction
	llll	h	total distance at end of last tour (km) * 2^16
	kkkk	h	total distance at end of last tour (km)
	oooo	h	next free memory offset
	yyyy	d	year of transfer
	mm	d	month of transfer
	dd	d	day of transfer

CM414M	????.????.oooo.dddd.uuuu.vvvv.????.????

	oooo	h	next free memory offset
	dddd	o	offset of last DD-record
	uuuu	h	1. total altitude up at end of last tour (m)
	vvvv	h	2. total altitude up at end of last tour (m)

---------------------------------------------------------------------------
725

HAC4	uuuu.dddd.aaaa.hhHH.ssmm.cccc.ddddd.eeee

	uuuu	h	total altitude up at end of last tour (m)
	dddd	h	total altitude down at end of last tour (m)
	aaaa	h	max altitude (m)
	hh	d	hour of total travel time
	HH	d	hour of total travel time * 100
	ss	d	seconds of total travel time
	mm	d	minute of total travel time
	cccc	o	offset of last CC-record
	dddd	o	offset of last DD-record
	eeee	o	offset of last compare record

CM414M	????.????.kkkk.llll.mmss.nntt.hh??.ii??

	kkkk	h	1. total distance at end of last tour (km)
	llll	h	2. total distance at end of last tour (km)
	mm	d	1. minute of total travel time
	ss	d	1. second of total travel time
	nn	d	2. minute of total travel time
	tt	d	2. second of total travel time
	hh	d	1. hour of total travel time
	ii	d	2. hour of total travel time
---------------------------------------------------------------------------


TOURDATA

The tour data is stored as an endless chain. If the end is reached the
unit begins  at the start and overwrites the data. The tour data
always has a record type that specifies what kind of data is
stored. The type is stored in bytes three and four as a hex-decimal
value. A new tour is introduced by a AA-record (byte three and four
have the value Ah). The tour data is made up by BB-records and the
tour end is marked by a CC- and a DD-record.

---------------------------------------------------------------------------
765+	ttTT.oooo.HHMM.mmdd.tttt.ssss.aaaa.pppp.
	  
	TT	h	"AA" (tour start)
	tt	h	type of tour
			"0E"	jogging (CM414M)
			"2E"	bike2 (CM414M)
			"3E"	bike1 (CM414M)
			"81"	jogging
			"91"	ski
			"A1"	bike
			"B1"	ski-bike
	oooo	o	DD-record offset
	HH	d	hour of tour
	MM	d	minute of tour
	mm	d	month of tour
	dd	d	day of tour
	tttt	h	total distance at tour start (km)
	ssss		distance msw
	aaaa	h	initial altitude (m)
	pppp	h	initial pulse (bpm)
---------------------------------------------------------------------------
765+	ttTT.mmcc.1111.2222.3333.4444.5555.6666.

	TT	h	"BB" (tour data)
	tt	h	temperature (°C)
	mm	h	marker (s)
	cc	h	cadence (rpm)
	1111 - 6666	h	values
---------------------------------------------------------------------------
765+	ttTT.mmcc.1111.2222.3333.4444.5555.6666.

	TT	h	"CC" (last tour data)
	tt	h	temperature (°C)
	mm	h	marker when recording exactly ended (s)
	cc	h	cadence (rpm)
	1111 - 6666	h	values
---------------------------------------------------------------------------
765+	ttTT.oooo.cccc.cccc.cccc.cccc.cccc.cccc.

	TT	h	"DD" (tour end)
	oooo	o	AA-record offset
	cccc	h	"0000"
---------------------------------------------------------------------------

The year information seems to be missing in the tour start
record. Observations of false intepreted files by the reference
software confirm this. The only way to guess a tour's year is to start
with the file's year, go through all tours by offset and to decrease
the year when the month of a tour is bigger than the one of the
previos tour. It seems kind of odd, since there are 16-bit spare bits
in every tour start record...


FILEEND

---------------------------------------------------------------------------
81925	cccc.

	cccc	h	checksum
---------------------------------------------------------------------------

One very interesting detail was left out until now, how the values are
stored. The AA-records store the initial values as hex-decimal
numbers. The BB-records store the temperature and cadence and the time
when a marker was set (relative to the record-time). The record-time
is calculated by the position of the record relative to the
start-record of the tour. Each record contains six values which are
stored every 20 seconds so it contains information about a period of
two minutes. 

Each value contains information about the pulse, altitude and
distance. The values are stored relative to the previous value (if the
pulse drops below 0, it is computed as 0 though). Since there are only
4 * 4 byte to store three numbers, range is limited.

value           width   range     factor 
                (bit)
---------------------------------------------------------------------------
pulse (bpm)     4       -8/+7	  x * 2
altitude (m)    6       -32/+31	  -16 <= x <= 16 : x
                                  x > 16         :  16 + (x - 16) * 7
                                  x < -16        : -16 + (x + 16) * 7
distance (m)    6       +63	  x * 10

Here is a sample c-function to compute these values (from
tourviewer.c):

/*
 * disassembles a data record
 *
 */
void WordToDataRec(Word value, DataRecPtr dataRecPtr) {
	// pulse (4)
	if (value & 0x8000)
		dataRecPtr->pulse = (((value & 0x7000) >> 12) - 16) * 2;
	else
		dataRecPtr->pulse = ((value & 0x7000) >> 12) * 2;

	// altitude (6)
	if (value & 0x0800) {
		dataRecPtr->altitude = ((value & 0x07C0) >> 6) - 32;
		if (dataRecPtr->altitude < -16)
			dataRecPtr->altitude = -16 + ((dataRecPtr->altitude + 
				16) * 7);
	} else {
		dataRecPtr->altitude = ((value & 0x0FC0) >> 6);
		if (dataRecPtr->altitude > 16)
			dataRecPtr->altitude = 16 + ((dataRecPtr->altitude - 
				16) * 7);
	}

	// distance (6)
	dataRecPtr->distance = value & 0x003F;
}


COPYRIGHT NOTES

All brand and product names are trademarks or registered trademarks of
their respective holders.

Copyright (C) 2000-2005 by Steffen Pingel.

Last change: 2005-07-01