/* ---- This file is part of SECONDO. Copyright (C) 2017, University in Hagen, Faculty of Mathematics and Computer Science, Database Systems for New Applications. SECONDO 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. SECONDO 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 SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- */ #include "aisdecode.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace aisdecode{ /* 1 Auxiliary functions */ const char chartbl[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ" "[\\]^- !\"#$%&`()*+,-./0123456789:;<=>?"; void trim(std::string& str, std::string whiteSpaces=" \r\n\t") { std::string::size_type pos = str.find_last_not_of(whiteSpaces); if(pos != std::string::npos) { str.erase(pos + 1); pos = str.find_first_not_of(whiteSpaces); if(pos != std::string::npos){ str.erase(0, pos); } } else { str.erase(str.begin(), str.end()); } } /* 2 Implementation of class Message */ template void Message::setMessage(const std::string& _msg){ msg = _msg; size_t ssize = getStringSize(); if(msg.size() < ssize){ std::cerr << "expected minimum size is " << ssize << std::endl; std::cerr << "got message of length " << msg.length() << std::endl; throw 1001; } createMsg(); } template void Message::createMsg(){ int s = getStringSize(); for(int i=0;i40) c = c - 8; message = (message << 6); message |= std::bitset((unsigned long)c); } } template std::string Message::extractString(int start, int end) const{ std::bitset b = ((message >> (size - (end+1) ) )); int len = (end - start) +1; assert(len % 6 == 0); // must be a multiple of 6 len = len / 6; // number of chars std::string chars(len,'@'); static std::bitset mask(std::string("111111")); for(int i=0;i> 6); } std::replace(chars.begin(), chars.end(),'@',' '); trim(chars); return chars; } template double Message::computeLat(unsigned int rlat) const{ if(rlat == 0x3412140){ // special value for nbot available return 91; } int lat = rlat; if(rlat >= 0x4000000){ lat = 0x8000000 - rlat; lat = -lat; } return lat/600000.0; } template double Message::computeLon(unsigned int rlon) const{ if(rlon == 0x6791AC0){ // special value return 181; } int lon = rlon; if(rlon >= 0x8000000){ lon = 0x10000000 - rlon; lon = -lon; } return lon / 600000.0; } template std::string Message::decodeText(const std::string& text) const{ std::string text2(text.length(),'@'); for(size_t i=0;i40) c = c-8; text2[i] = chartbl[(int)c]; } return text2; } /* 3 Implementation of MultiLineMessage */ MultiLineMessage::MultiLineMessage(int fragmentcount, int fragmentnumber, const std::string& fragment, int messageId, const std::string& rcc){ for(int i=0;imessage_Id = messageId; this->rcc = rcc; } void MultiLineMessage::add(int fragmentcount, int fragmentnumber, const std::string& fragment, int messageId, const std::string& rcc){ if( ((size_t) fragmentcount != fragments.size()) || (fragmentnumber < 1) || ((size_t) fragmentnumber > fragments.size()) || (fragments[fragmentnumber-1] !="") || (this->rcc != rcc) ){ throw 1002; } fragments[fragmentnumber-1] = fragment; fragmentsAvailable++; } std::string MultiLineMessage::getMessage() const{ assert(isComplete()); std::stringstream ss; for(size_t i=0;i28){ std::cout << "more information available " << std::endl; } std::cout << "----------------------------------------------" << std::endl; } void Message1_3::extractInfos(){ messageType = extract(0,5); repeatIndicator = extract(6,7); mmsi = extract(8,37); status = extract(38,41); rot = extract(42,49); sog = extract(50,59); accuracy = extract(60,60); unsigned int lon = extract(61,88); longitude = computeLon(lon); unsigned int lat = extract(89,115); latitude = computeLat(lat); cog = extract(116,127); heading = extract(128,136); second = extract(137,142); maneuver = extract(143,144); spare = extract(145,147); raim = extract(148,148); rstatus = extract(149,167); } /* 4 Message4 */ void Message4::print()const{ std::cout << "--------------------------------------------" << std::endl; std::cout << "type " << type << std::endl; std::cout << "repeat " << repeat << std::endl; std::cout << "mmsi " << mmsi << std::endl; std::cout << "year " << year << std::endl; std::cout << "month " << month << std::endl; std::cout << "day " << day << std::endl; std::cout << "hour " << hour << std::endl; std::cout << "minute " << minute << std::endl; std::cout << "second " << second << std::endl; std::cout << "fix " << fix << std::endl; std::cout << "longitude " << (longitude / 600000.0) << std::endl; std::cout << "latitude " << (latitude / 600000.0) << std::endl; std::cout << "epfd " << epfd << std::endl; std::cout << "spare " << spare << std::endl; std::cout << "raim " << raim << std::endl; std::cout << "sotdma " << sotdma << std::endl; if(msg.length()>28){ std::cout << "more information available " << std::endl; } std::cout << "--------------------------------------------" << std::endl; } void Message4::extractInfos(){ type = extract(0,5); repeat = extract(6,7); mmsi = extract(8,37); year = extract(38,51); month = extract(52,55); day = extract(56,60); hour = extract(61,65); minute = extract(66,71); second = extract(72,77); fix = extract(78,78); longitude = computeLon(extract(79,106)); latitude = computeLat(extract(107,133)); epfd = extract(134,137); spare = extract(138,147); raim = extract(148,148); sotdma = extract(149,167); assert(type==4); } /* 5 Message5 */ void Message5::print() const{ std::cout << "--------------------------------------" << std::endl; std::cout << "type : " << type << std::endl; std::cout << "repeat : " << repeat << std::endl; std::cout << "mmsi :" << mmsi << std::endl; std::cout << "ais_version " << ais_version << std::endl; std::cout << "imo " << imo << std::endl; std::cout << "callSign " << callSign << std::endl; std::cout << "vesselName " << vesselName << std::endl; std::cout << "shiptype " << shipType << std::endl; std::cout << "dimToBow " << dimToBow << std::endl; std::cout << "dimToStern " << dimToStern << std::endl; std::cout << "dimToPort " << dimToPort << std::endl; std::cout << "dimToStarboard" << dimToStarboard << std::endl; std::cout << "epfd " << epfd << std::endl; std::cout << "month " << month << std::endl; std::cout << "day " << day << std::endl; std::cout << "hour " << hour << std::endl; std::cout << "minute " << minute << std::endl; std::cout << "draught " << draught << std::endl; std::cout << "destination " << destination << std::endl; std::cout << "dte " << dte << std::endl; std::cout << "--------------------------------------" << std::endl; } void Message5::extractInfos(){ type = extract(0,5); repeat = extract(6,7); mmsi = extract(8,37); ais_version = extract(38,39); imo = extract(40,69); callSign = extractString(70,111); vesselName = extractString(112,231); shipType = extract(232,239); dimToBow = extract(240,248); dimToStern = extract(249,257); dimToPort = extract(258,263); dimToStarboard = extract(264,269); epfd = extract(270,273); month = extract(274,277); day = extract(278,282); hour = extract(283,287); minute = extract(288,293); draught = extract(294,301); destination = extractString(302,421); dte = extract(422,422); assert(type==5); } /* 6 Message9 */ void Message9::print()const{ std::cout << "type " << type << std::endl; std::cout << "repeat " << repeat << std::endl; std::cout << "mmsi " << mmsi << std::endl; std::cout << "alt " << alt << std::endl; std::cout << "sog " << sog << std::endl; std::cout << "accuracy " << accuracy << std::endl; std::cout << "longitude " << longitude << std::endl; std::cout << "latitude " << latitude << std::endl; std::cout << "cog " << cog << std::endl; std::cout << "second " << second << std::endl; std::cout << "reserved " << reserved << std::endl; std::cout << "dte " << dte << std::endl; std::cout << "assigned " << assigned << std::endl; std::cout << "raim " << raim << std::endl; std::cout << "radio " << radio << std::endl; } void Message9::extractInfos(){ type = extract(0,5); repeat = extract(6,7); mmsi = extract(30,37); alt = extract(38,49); sog = extract(50,59); accuracy = extract(60,60); longitude = computeLon(extract(61,88)); latitude = computeLat(extract(89,115)); cog = extract(116,127); second = extract(128,133); reserved = extract(134,141); dte = extract(142,142); assigned = extract(146,146); raim = extract(147,147); radio = extract(148,167); assert(type==9); } /* 8 Message12 */ void Message12::print()const{ std::cout << "-----------------------------------" << std::endl; std::cout << "type " << type << std::endl; std::cout << "repeat " << repeat << std::endl; std::cout << "source " << source_mmsi << std::endl; std::cout << "sequence number " << sequence_number << std::endl; std::cout << "dest " <pubsetbuf(buffer, FILE_BUFFER_SIZE); } } MessageBase* aisdecoder::getNextMessage(){ while(!in.eof() && in.good()){ getline(in,line); MessageBase* msg = 0; try{ msg = decodeLine(line,0); } catch(...){ std::cerr << "Exception during decoding line " << line << std::endl; } if(msg){ return msg; } } try{ in.close(); } catch(...) {} return 0; } MessageBase* aisdecoder::getNextMessage(const int type){ while(!in.eof() && in.good()){ getline(in,line); MessageBase* msg = 0; try{ msg = decodeLine(line,type); } catch(...){ std::cerr << "Exception during decoding line " << line << std::endl; } if(msg){ return msg; } } try{ in.close(); } catch(...) {} return 0; } MessageBase* aisdecoder::decodeLine(const std::string& line, const int type){ if(line.size()==0){ // ignore empty lines return 0; } size_t pos = line.find(","); if(pos==std::string::npos){ std::cerr << "invalid line format, no comma inside" << std::endl; std::cerr << "line is " << line << std::endl; return 0; } std::string talker = line.substr(0,pos); if(talker.length()==6 && talker[0]=='!' && talker.substr(3,2)=="VD"){ return decodevdms(line, type); } else if(talker=="$PVOL"){ return 0; // heartbeat message, ignore } else { std::cerr << "unknown talker: " << talker << std::endl; return 0; } } MessageBase* aisdecoder::decodevdms(const std::string& line, const int type){ std::string talker = line.substr(1,2); size_t pos1 = 0; size_t pos2 = line.find(","); std::string tmp; if(!getNextPart(line,pos1,pos2,tmp)){ std::cerr << "cannot extract framecount" << std::endl; std::cerr << "line is " << line << std::endl; return 0; } int fragmentcount = str2int(tmp); if(!getNextPart(line,pos1,pos2,tmp)){ std::cerr << "cannot extract frame number in line '" << line << "'" << std::endl; std::cerr << "line is " << line << std::endl; return 0; } int fragmentnumber = str2int(tmp); if(!getNextPart(line,pos1,pos2,tmp)){ std::cerr << "cannot extract message id" << std::endl; std::cerr << "line is " << line << std::endl; return 0; } int messageId = str2int(tmp); if(!getNextPart(line,pos1,pos2,tmp)){ std::cerr << "cannot extract radio channel code" << std::endl; std::cerr << "line is " << line << std::endl; return 0; } std::string rcc = tmp; if(!getNextPart(line,pos1,pos2,tmp)){ std::cerr << "cannot extract message " << std::endl; std::cerr << "line is " << line << std::endl; return 0; } std::string msg = tmp; if(msg.length()<1){ return 0; } // extract type and filter out if type not fits int t = msg[0] - '0'; if(t>40) t -= 8; if(type>0 && t!=type){ return 0; } pos1 = pos2; pos1++; pos2 = line.find(",",pos1); std::string fill = pos2==std::string::npos ? line.substr(pos1, std::string::npos) : line.substr(pos1, pos2-pos1); if(fill.size()!=4){ std::cerr << "invalid length of fill" << std::endl; std::cerr << "fill is " << fill << std::endl; std::cerr << "line is " << line << std::endl; return 0; } if( fill[1] != '*'){ std::cerr << "missing '*' in last field" << std::endl; std::cerr << "line is " << line << std::endl; return 0; } int check = atoi(("0x"+fill.substr(2)).c_str()); std::string rest = pos2 == std::string::npos ? line.substr(pos1,std::string::npos) : ""; return decodeLine(talker, fragmentcount, fragmentnumber, messageId, rcc, msg, fill[0]-'0', check, rest); } MessageBase* aisdecoder::decodeLine(std::string& talker, int fragmentcount, int fragmentnumber, int messageId, std::string& rcc, std::string& msg, int fill, int check, std::string& rest){ if(fragmentcount > 1){ if(incompleteMessages.find(messageId)==incompleteMessages.end()){ MultiLineMessage mlm(fragmentcount, fragmentnumber, msg, messageId,rcc); incompleteMessages[messageId] = mlm; } else { try{ incompleteMessages[messageId].add(fragmentcount, fragmentnumber, msg, messageId,rcc); } catch(int err){ incompleteMessages.erase(messageId); MultiLineMessage mlm(fragmentcount, fragmentnumber, msg, messageId,rcc); incompleteMessages[messageId] = mlm; } } MultiLineMessage mlm = incompleteMessages[messageId]; if(mlm.isComplete()){ incompleteMessages.erase(messageId); return decode(mlm); } return 0; } if(msg.length() < 1){ std::cerr << "empty message" << std::endl; return 0; } int messagenumber = msg[0] - 48; if(messagenumber > 40){ messagenumber = messagenumber - 8; } if(messagenumber <1 || messagenumber > 27){ std::cerr << "invalid message number " << messagenumber << std::endl; return 0; } return decode(messagenumber, msg); } MessageBase* aisdecoder::decode(const MultiLineMessage& mlm){ assert(mlm.isComplete()); std::string msg = mlm.getMessage(); if(msg.size()<1){ std::cerr << "found empty multi line message"; return 0; } int messagenumber = msg[0] - 48; if(messagenumber > 40){ messagenumber = messagenumber - 8; } return decode(messagenumber,msg); } MessageBase* aisdecoder::decode(const int messagenumber, const std::string& msg) { try{ switch(messagenumber){ case 1: case 2: case 3: return new Message1_3(msg); case 4: return new Message4(msg); case 5: return new Message5(msg); case 9: return new Message9(msg); case 12: return new Message12(msg); case 14: return new Message14(msg); case 18: return new Message18(msg); case 19: return new Message19(msg); case 24: return new Message24(msg); default: std::cerr << "aisdecoder: message type " << messagenumber << "not implemented yet" << std::endl; return 0; } } catch(int ex){ std::cerr << "exception during processing message " << messagenumber << std::endl; std::cerr << "message is " << msg << std::endl; std::cerr << "message's length : " << msg.length() << std::endl; std::cerr << "Exception is " << ex << std::endl; return 0; } catch(...){ std::cerr << " unknown exception during processing message " << messagenumber << std::endl; return 0; } return 0; } bool aisdecoder::getNextPart(const std::string& line, size_t& pos1, size_t&pos2, std::string& result){ pos1 = pos2; pos1++; pos2 = line.find(",",pos1); if(pos2==std::string::npos){ result = ""; return false; } result = line.substr(pos1, pos2-pos1); return true; } } // end of namespace