aisdecoder/src/lib/protodec.c

370 lines
8.1 KiB
C

/*
* protodec.c
*
* (c) Ruben Undheim 2008
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <time.h>
#include <string.h> /* String function definitions */
#include "callbacks.h"
#include "config.h"
#include "protodec.h"
#include "hmalloc.h"
decoder_on_nmea_sentence_received on_nmea_sentence_received=NULL;
#ifdef DMALLOC
#include <dmalloc.h>
#endif
void protodec_initialize(struct demod_state_t *d, struct serial_state_t *serial, char chanid)
{
memset(d, 0, sizeof(struct demod_state_t));
d->chanid = chanid;
d->serial = serial;
d->receivedframes = 0;
d->lostframes = 0;
d->lostframes2 = 0;
protodec_reset(d);
d->seqnr = 0;
d->buffer = hmalloc(DEMOD_BUFFER_LEN);
d->rbuffer = hmalloc(DEMOD_BUFFER_LEN);
d->nmea = hmalloc(NMEABUFFER_LEN);
}
void protodec_deinit(struct demod_state_t *d)
{
hfree(d->buffer);
hfree(d->rbuffer);
hfree(d->nmea);
}
void protodec_reset(struct demod_state_t *d)
{
d->state = ST_SKURR;
d->nskurr = 0;
d->ndata = 0;
d->npreamble = 0;
d->nstartsign = 0;
d->nstopsign = 0;
d->antallpreamble = 0;
d->antallenner = 0;
d->last = 0;
d->bitstuff = 0;
d->bufferpos = 0;
}
/*
* Calculates CRC-checksum
*/
unsigned short protodec_sdlc_crc(const unsigned char *data, unsigned len)
{
unsigned short c, crc = 0xffff;
while (len--)
for (c = 0x100 + *data++; c > 1; c >>= 1)
if ((crc ^ c) & 1)
crc = (crc >> 1) ^ 0x8408;
else
crc >>= 1;
return ~crc;
}
int protodec_calculate_crc(int length_bits, struct demod_state_t *d)
{
int length_bytes;
unsigned char *buf;
int buflen;
int i, j, x;
unsigned char tmp;
if (length_bits <= 0) {
return 0;
}
length_bytes = length_bits / 8;
buflen = length_bytes + 2;
/* what is this? */
buf = (unsigned char *) hmalloc(sizeof(*buf) * buflen);
for (j = 0; j < buflen; j++) {
tmp = 0;
for (i = 0; i < 8; i++)
tmp |= (((d->buffer[i + 8 * j]) << (i)));
buf[j] = tmp;
}
/* ok, here's the actual CRC calculation */
unsigned short crc = protodec_sdlc_crc(buf, buflen);
//DBG(printf("CRC: %04x\n",crc));
/* what is this? */
memset(d->rbuffer, 0, DEMOD_BUFFER_LEN);
for (j = 0; j < length_bytes; j++) {
for (i = 0; i < 8; i++) {
x = j * 8 + i;
if (x >= DEMOD_BUFFER_LEN) {
hfree(buf);
return 0;
} else {
d->rbuffer[x] = (buf[j] >> (7 - i)) & 1;
}
}
}
hfree(buf);
return (crc == 0x0f47);
}
unsigned long protodec_henten(int from, int size, unsigned char *frame)
{
int i = 0;
unsigned long tmp = 0;
for (i = 0; i < size; i++)
tmp |= (frame[from + i]) << (size - 1 - i);
return tmp;
}
void protodec_generate_nmea(struct demod_state_t *d, int bufferlen, int fillbits, time_t received_t)
{
int senlen;
int pos;
int k, offset;
int m;
unsigned char sentences, sentencenum, nmeachk, letter;
//6bits to nmea-ascii. One sentence len max 82char
//inc. head + tail.This makes inside datamax 62char multipart, 62 single
senlen = 56; //this is normally not needed.For testing only. May be fixed number
if (bufferlen <= (senlen * 6)) {
sentences = 1;
} else {
sentences = bufferlen / (senlen * 6);
//sentences , if overflow put one more
if (bufferlen % (senlen * 6) != 0)
sentences++;
};
sentencenum = 0;
pos = 0;
offset = (sentences>1) ? 15 : 14;
do {
k = offset; //leave room for nmea header
while (k < senlen + offset && bufferlen > pos) {
letter = (unsigned char)protodec_henten(pos, 6, d->rbuffer);
// 6bit-to-ascii conversion by IEC
letter += (letter < 40) ? 48 : 56;
d->nmea[k] = letter;
pos += 6;
k++;
}
sentencenum++;
memcpy(&d->nmea[0], "!AIVDM,0,0,", 11);
d->nmea[7] += sentences;
d->nmea[9] += sentencenum;
memcpy(&d->nmea[k], ",0*00\0", 6);
if (sentences > 1) {
d->nmea[11] = '0' + d->seqnr;
d->nmea[12] = ',';
d->nmea[13] = d->chanid;
d->nmea[14] = ',';
if (sentencenum == sentences) d->nmea[k + 1] = '0' + fillbits;
} else {
d->nmea[11] = ',';
d->nmea[12] = d->chanid;
d->nmea[13] = ',';
}
m = 1;
nmeachk = d->nmea[m++];
while (d->nmea[m] != '*') nmeachk ^= d->nmea[m++];
sprintf(&d->nmea[k + 3], "%02X\r\n", nmeachk);
if (on_nmea_sentence_received != NULL)
on_nmea_sentence_received(d->nmea, k+7, sentences, sentencenum);
} while (sentencenum < sentences);
}
void protodec_getdata(int bufferlen, struct demod_state_t *d)
{
unsigned char type = protodec_henten(0, 6, d->rbuffer);
if (type < 1 || type > MAX_AIS_PACKET_TYPE /* 9 */)
return;
// unsigned long mmsi = protodec_henten(8, 30, d->rbuffer);
int fillbits = 0;
int k;
time_t received_t;
time(&received_t);
if (bufferlen % 6 > 0) {
fillbits = 6 - (bufferlen % 6);
for (k = bufferlen; k < bufferlen + fillbits; k++)
d->rbuffer[k] = 0;
bufferlen = bufferlen + fillbits;
}
protodec_generate_nmea(d, bufferlen, fillbits, received_t);
d->seqnr++;
if (d->seqnr > 9)
d->seqnr = 0;
if (type < 1 || type > MAX_AIS_PACKET_TYPE)
return; // unsupported packet type
}
void protodec_decode(char *in, int count, struct demod_state_t *d)
{
int i = 0;
int bufferlength, correct;
while (i < count) {
switch (d->state) {
case ST_DATA:
if (d->bitstuff) {
if (in[i] == 1) {
d->state = ST_STOPSIGN;
d->ndata = 0;
d->bitstuff = 0;
} else {
d->ndata++;
d->last = in[i];
d->bitstuff = 0;
}
} else {
if (in[i] == d->last && in[i] == 1) {
d->antallenner++;
if (d->antallenner == 4) {
d->bitstuff = 1;
d->antallenner = 0;
}
} else
d->antallenner = 0;
d->buffer[d->bufferpos] = in[i];
d->bufferpos++;
d->ndata++;
if (d->bufferpos >= 449) {
protodec_reset(d);
}
}
break;
case ST_SKURR:
if (in[i] != d->last)
d->antallpreamble++;
else
d->antallpreamble = 0;
d->last = in[i];
if (d->antallpreamble > 14 && in[i] == 0) {
d->state = ST_PREAMBLE;
d->nskurr = 0;
d->antallpreamble = 0;
}
d->nskurr++;
break;
case ST_PREAMBLE:
if (in[i] != d->last && d->nstartsign == 0) {
d->antallpreamble++;
} else {
if (in[i] == 1) {
if (d->nstartsign == 0) {
d->nstartsign = 3;
d->last = in[i];
} else if (d->nstartsign == 5) {
d->nstartsign++;
d->npreamble = 0;
d->antallpreamble = 0;
d->state = ST_STARTSIGN;
} else {
d->nstartsign++;
}
} else {
if (d->nstartsign == 0) {
d->nstartsign = 1;
} else {
protodec_reset(d);
}
}
}
d->npreamble++;
break;
case ST_STARTSIGN:
if (d->nstartsign >= 7) {
if (in[i] == 0) {
d->state = ST_DATA;
d->nstartsign = 0;
d->antallenner = 0;
memset(d->buffer, 0, DEMOD_BUFFER_LEN);
d->bufferpos = 0;
} else {
protodec_reset(d);
}
} else if (in[i] == 0) {
protodec_reset(d);
}
d->nstartsign++;
break;
case ST_STOPSIGN:
bufferlength = d->bufferpos - 6 - 16;
if (in[i] == 0 && bufferlength > 0) {
correct = protodec_calculate_crc(bufferlength, d);
if (correct) {
d->receivedframes++;
protodec_getdata(bufferlength, d);
} else {
d->lostframes++;
}
} else {
d->lostframes2++;
}
protodec_reset(d);
break;
}
d->last = in[i];
i++;
}
}