Eine Beschreibung des Rohdaten-Formates befindet sich im CVS
im Verzeichnis
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