1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
Copyright 2014 Aaron Gong Hsien-Joen <[email protected]> |
4
|
|
|
|
5
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); |
6
|
|
|
you may not use this file except in compliance with the License. |
7
|
|
|
You may obtain a copy of the License at |
8
|
|
|
|
9
|
|
|
http://www.apache.org/licenses/LICENSE-2.0 |
10
|
|
|
|
11
|
|
|
Unless required by applicable law or agreed to in writing, software |
12
|
|
|
distributed under the License is distributed on an "AS IS" BASIS, |
13
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14
|
|
|
See the License for the specific language governing permissions and |
15
|
|
|
limitations under the License. |
16
|
|
|
*/ |
17
|
|
|
/* |
18
|
|
|
Modified in 2017 by Ycarus <[email protected]> |
19
|
|
|
Original version come from https://github.com/ais-one/phpais |
20
|
|
|
*/ |
21
|
|
|
class AIS { |
22
|
|
|
/* AIS Decoding |
23
|
|
|
- Receive and get ITU payload |
24
|
|
|
- Organises the binary bits of the Payload into 6-bit strings, |
25
|
|
|
- Converts the 6-bit strings into their representative "valid characters" – see IEC 61162-1, table 7, |
26
|
|
|
- Assembles the valid characters into an encapsulation string, and |
27
|
|
|
- Transfers the encapsulation string using the VDM sentence formatter. |
28
|
|
|
*/ |
29
|
|
|
|
30
|
|
|
private function make_latf($temp) { // unsigned long |
31
|
|
|
$flat; // float |
|
|
|
|
32
|
|
|
$temp = $temp & 0x07FFFFFF; |
33
|
|
|
if ($temp & 0x04000000) { |
34
|
|
|
$temp = $temp ^ 0x07FFFFFF; |
35
|
|
|
$temp += 1; |
36
|
|
|
$flat = (float)($temp / (60.0 * 10000.0)); |
37
|
|
|
$flat *= -1.0; |
38
|
|
|
} else $flat = (float)($temp / (60.0 * 10000.0)); |
39
|
|
|
return $flat; // float |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
private function make_lonf($temp) { // unsigned long |
43
|
|
|
$flon; // float |
|
|
|
|
44
|
|
|
$temp = $temp & 0x0FFFFFFF; |
45
|
|
|
if ($temp & 0x08000000) { |
46
|
|
|
$temp = $temp ^ 0x0FFFFFFF; |
47
|
|
|
$temp += 1; |
48
|
|
|
$flon = (float)($temp / (60.0 * 10000.0)); |
49
|
|
|
$flon *= -1.0; |
50
|
|
|
} else $flon = (float)($temp / (60.0 * 10000.0)); |
51
|
|
|
return $flon; |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
private function ascii_2_dec($chr) { |
55
|
|
|
$dec=ord($chr);//get decimal ascii code |
56
|
|
|
$hex=dechex($dec);//convert decimal to hex |
|
|
|
|
57
|
|
|
return ($dec); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/* |
61
|
|
|
$ais_map64 = array( |
62
|
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // 48 |
63
|
|
|
':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', |
64
|
|
|
'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', |
65
|
|
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', // 87 |
66
|
|
|
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', // 96 |
67
|
|
|
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', |
68
|
|
|
't', 'u', 'v', 'w' // 119 |
69
|
|
|
); // char 64 |
70
|
|
|
*/ |
71
|
|
|
private function asciidec_2_8bit($ascii) { |
72
|
|
|
//only process in the following range: 48-87, 96-119 |
73
|
|
|
if ($ascii < 48) { } |
|
|
|
|
74
|
|
|
else { |
75
|
|
|
if($ascii>119) { } |
|
|
|
|
76
|
|
|
else { |
77
|
|
|
if ($ascii>87 && $ascii<96) ; |
78
|
|
|
else { |
79
|
|
|
$ascii=$ascii+40; |
80
|
|
|
if ($ascii>128){ |
81
|
|
|
$ascii=$ascii+32; |
82
|
|
|
} else { |
83
|
|
|
$ascii=$ascii+40; |
84
|
|
|
} |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
return ($ascii); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
private function dec_2_6bit($dec) { |
92
|
|
|
$bin=decbin($dec); |
93
|
|
|
return(substr($bin, -6)); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
private function binchar($_str, $_start, $_size) { |
97
|
|
|
// ' ' --- '?', // 0x20 - 0x3F |
98
|
|
|
// '@' --- '_', // 0x40 - 0x5F |
99
|
|
|
$ais_chars = array( |
100
|
|
|
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', |
101
|
|
|
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', |
102
|
|
|
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', |
103
|
|
|
'^', '_', ' ', '!', '\"', '#', '$', '%', '&', '\'', |
104
|
|
|
'(', ')', '*', '+', ',', '-', '.', '/', '0', '1', |
105
|
|
|
'2', '3', '4', '5', '6', '7', '8', '9', ':', ';', |
106
|
|
|
'<', '=', '>', '?' |
107
|
|
|
); |
108
|
|
|
$rv = ''; |
109
|
|
|
if ($_size % 6 == 0) { |
110
|
|
|
$len = $_size / 6; |
111
|
|
|
for ($i=0; $i<$len; $i++) { |
112
|
|
|
$offset = $i * 6; |
113
|
|
|
$rv .= $ais_chars[ bindec(substr($_str,$_start + $offset,6)) ]; |
114
|
|
|
} |
115
|
|
|
} |
116
|
|
|
return $rv; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
// function for decoding the AIS Message ITU Payload |
120
|
|
|
private function decode_ais($_aisdata) { |
121
|
|
|
$ro = new stdClass(); // return object |
122
|
|
|
$ro->cls = 0; // AIS class undefined, also indicate unparsed msg |
123
|
|
|
$ro->name = ''; |
124
|
|
|
$ro->status = ''; |
125
|
|
|
$ro->sog = -1.0; |
126
|
|
|
$ro->cog = 0.0; |
127
|
|
|
$ro->lon = 0.0; |
128
|
|
|
$ro->lat = 0.0; |
129
|
|
|
$ro->ts = time(); |
130
|
|
|
$ro->id = bindec(substr($_aisdata,0,6)); |
131
|
|
|
$ro->mmsi = bindec(substr($_aisdata,8,30)); |
132
|
|
|
if ($ro->id >= 1 && $ro->id <= 3) { |
133
|
|
|
$ro->cog = bindec(substr($_aisdata,116,12))/10; |
134
|
|
|
$ro->sog = bindec(substr($_aisdata,50,10))/10; |
135
|
|
|
$ro->lon = $this->make_lonf(bindec(substr($_aisdata,61,28))); |
136
|
|
|
$ro->lat = $this->make_latf(bindec(substr($_aisdata,89,27))); |
137
|
|
|
$ro->cls = 1; // class A |
138
|
|
|
} else if ($ro->id == 5) { |
139
|
|
|
//$imo = bindec(substr($_aisdata,40,30)); |
140
|
|
|
//$cs = $this->binchar($_aisdata,70,42); |
141
|
|
|
$ro->name = $this->binchar($_aisdata,112,120); |
142
|
|
|
$ro->cls = 1; // class A |
143
|
|
|
} else if ($ro->id == 18) { |
144
|
|
|
$ro->cog = bindec(substr($_aisdata,112,12))/10; |
145
|
|
|
$ro->sog = bindec(substr($_aisdata,46,10))/10; |
146
|
|
|
$ro->lon = $this->make_lonf(bindec(substr($_aisdata,57,28))); |
147
|
|
|
$ro->lat = $this->make_latf(bindec(substr($_aisdata,85,27))); |
148
|
|
|
$ro->cls = 2; // class B |
149
|
|
|
} else if ($ro->id == 19) { |
150
|
|
|
$ro->cog = bindec(substr($_aisdata,112,12))/10; |
151
|
|
|
$ro->sog = bindec(substr($_aisdata,46,10))/10; |
152
|
|
|
$ro->lon = $this->make_lonf(bindec(substr($_aisdata,61,28))); |
153
|
|
|
$ro->lat = $this->make_latf(bindec(substr($_aisdata,89,27))); |
154
|
|
|
$ro->name = $this->binchar($_aisdata,143,120); |
155
|
|
|
$ro->cls = 2; // class B |
156
|
|
|
} else if ($ro->id == 24) { |
157
|
|
|
$pn = bindec(substr($_aisdata,38,2)); |
158
|
|
|
if ($pn == 0) { |
159
|
|
|
$ro->name = $this->binchar($_aisdata,40,120); |
160
|
|
|
} |
161
|
|
|
$ro->cls = 2; // class B |
162
|
|
|
} |
163
|
|
|
$ro->statusid = bindec(substr($_aisdata,38,4)); |
164
|
|
|
$ro->status = $this->getStatus($ro->statusid); |
165
|
|
|
//var_dump($ro); // dump results here for demo purpose |
166
|
|
|
return $ro; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
public function getStatus($statusid) { |
170
|
|
|
if ($statusid == 0) { |
171
|
|
|
return 'under way using engine'; |
172
|
|
|
} elseif ($statusid == 1) { |
173
|
|
|
return 'at anchor'; |
174
|
|
|
} elseif ($statusid == 2) { |
175
|
|
|
return 'not under command'; |
176
|
|
|
} elseif ($statusid == 3) { |
177
|
|
|
return 'restricted maneuverability'; |
178
|
|
|
} elseif ($statusid == 4) { |
179
|
|
|
return 'constrained by her draught'; |
180
|
|
|
} elseif ($statusid == 5) { |
181
|
|
|
return 'moored'; |
182
|
|
|
} elseif ($statusid == 6) { |
183
|
|
|
return 'aground'; |
184
|
|
|
} elseif ($statusid == 7) { |
185
|
|
|
return 'engaged in fishing'; |
186
|
|
|
} elseif ($statusid == 8) { |
187
|
|
|
return 'under way sailing'; |
188
|
|
|
} elseif ($statusid == 9) { |
189
|
|
|
return 'reserved for future amendment of navigational status for ships carrying DG, HS, or MP, or IMO hazard or pollutant category C, high speed craft (HSC)'; |
190
|
|
|
} elseif ($statusid == 10) { |
191
|
|
|
return 'reserved for future amendment of navigational status for ships carrying dangerous goods (DG), harmful substances (HS) or marine pollutants (MP), or IMO hazard or pollutant category A, wing in ground (WIG)'; |
192
|
|
|
} elseif ($statusid == 11) { |
193
|
|
|
return 'power-driven vessel towing astern (regional use)'; |
194
|
|
|
} elseif ($statusid == 12) { |
195
|
|
|
return 'power-driven vessel pushing ahead or towing alongside (regional use)'; |
196
|
|
|
} elseif ($statusid == 13) { |
197
|
|
|
return 'reserved for future use'; |
198
|
|
|
} elseif ($statusid == 14) { |
199
|
|
|
return 'AIS-SART (active), MOB-AIS, EPIRB-AIS'; |
200
|
|
|
} elseif ($statusid == 15) { |
201
|
|
|
return 'undefined = default (also used by AIS-SART, MOB-AIS and EPIRB-AIS under test)'; |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
public function process_ais_itu($_itu, $_len, $_filler, $aux /*, $ais_ch*/) { |
|
|
|
|
206
|
|
|
global $port; // tcpip port... |
207
|
|
|
|
208
|
|
|
static $debug_counter = 0; |
209
|
|
|
$aisdata168='';//six bit array of ascii characters |
210
|
|
|
$ais_nmea_array = str_split($_itu); // convert to an array |
211
|
|
|
foreach ($ais_nmea_array as $value) { |
212
|
|
|
$dec = $this->ascii_2_dec($value); |
213
|
|
|
$bit8 = $this->asciidec_2_8bit($dec); |
214
|
|
|
$bit6 = $this->dec_2_6bit($bit8); |
215
|
|
|
//echo $value ."-" .$bit6 .""; |
216
|
|
|
$aisdata168 .=$bit6; |
217
|
|
|
} |
218
|
|
|
//echo $aisdata168 . "<br/>"; |
219
|
|
|
return $this->decode_ais($aisdata168, $aux); |
|
|
|
|
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
// char* - AIS \r terminated string |
223
|
|
|
// TCP based streams which send messages in full can use this instead of calling process_ais_buf |
224
|
|
|
public function process_ais_raw($rawdata, $aux = '') { // return int |
225
|
|
|
static $num_seq; // 1 to 9 |
226
|
|
|
static $seq; // 1 to 9 |
227
|
|
|
static $pseq; // previous seq |
228
|
|
|
static $msg_sid = -1; // 0 to 9, indicate -1 at start state of device, do not process messages |
229
|
|
|
static $cmsg_sid; // current msg_sid |
230
|
|
|
static $itu; // buffer for ITU message |
231
|
|
|
|
232
|
|
|
$filler = 0; // fill bits (int) |
233
|
|
|
$chksum = 0; |
234
|
|
|
// raw data without the \n |
235
|
|
|
// calculate checksum after ! till * |
236
|
|
|
// assume 1st ! is valid |
237
|
|
|
// find * ensure that it is at correct position |
238
|
|
|
$end = strrpos ( $rawdata , '*' ); |
239
|
|
|
if ($end === FALSE) return -1; // check for NULLS!!! |
240
|
|
|
$cs = substr( $rawdata, $end + 1 ); |
241
|
|
|
if ( strlen($cs) != 2 ) return -1; // correct cs length |
242
|
|
|
$dcs = (int)hexdec( $cs ); |
243
|
|
|
for ( $alias=1; $alias<$end; $alias++) $chksum ^= ord( $rawdata[$alias] ); // perform XOR for NMEA checksum |
244
|
|
|
if ( $chksum == $dcs ) { // NMEA checksum pass |
245
|
|
|
$pcs = explode(',', $rawdata); |
246
|
|
|
// !AI??? identifier |
247
|
|
|
$num_seq = (int)$pcs[1]; // number of sequences |
248
|
|
|
$seq = (int)$pcs[2]; // get sequence |
249
|
|
|
// get msg sequence id |
250
|
|
|
if ($pcs[3] == '') $msg_sid = -1; // non-multipart message, set to -1 |
251
|
|
|
else $msg_sid = (int)$pcs[3]; // multipart message |
252
|
|
|
$ais_ch = $pcs[4]; // get AIS channel |
|
|
|
|
253
|
|
|
// message sequence checking |
254
|
|
|
if ($num_seq < 1 || $num_seq > 9) { |
255
|
|
|
echo "ERROR,INVALID_NUMBER_OF_SEQUENCES ".time()." $rawdata\n"; |
256
|
|
|
return -1; |
257
|
|
|
} else if ($seq < 1 || $seq > 9) { // invalid sequences number |
258
|
|
|
echo "ERROR,INVALID_SEQUENCES_NUMBER ".time()." $rawdata\n"; |
259
|
|
|
return -1; |
260
|
|
|
} else if ($seq > $num_seq) { |
261
|
|
|
echo "ERROR,INVALID_SEQUENCE_NUMBER_OR_INVALID_NUMBER_OF_SEQUENCES ".time()." $rawdata\n"; |
262
|
|
|
return -1; |
263
|
|
|
} else { // sequencing ok, handle single/multi-part messaging |
264
|
|
|
if ($seq == 1) { // always init to 0 at first sequence |
265
|
|
|
$filler = 0; // ? |
266
|
|
|
$itu = ""; // init message length |
267
|
|
|
$pseq = 0; // note previous sequence number |
268
|
|
|
$cmsg_sid = $msg_sid; // note msg_sid |
269
|
|
|
} |
270
|
|
|
if ($num_seq > 1) { // for multipart messages |
271
|
|
|
if ($cmsg_sid != $msg_sid // different msg_sid |
272
|
|
|
|| $msg_sid == -1 // invalid initial msg_sid |
273
|
|
|
|| ($seq - $pseq) != 1 // not insequence |
274
|
|
|
) { // invalid for multipart message |
275
|
|
|
$msg_sid = -1; |
276
|
|
|
$cmsg_sid = -1; |
277
|
|
|
echo "ERROR,INVALID_MULTIPART_MESSAGE ".time()." $rawdata\n"; |
278
|
|
|
return -1; |
279
|
|
|
} else { |
280
|
|
|
$pseq++; |
281
|
|
|
} |
282
|
|
|
} |
283
|
|
|
$itu = $itu.$pcs[5]; // get itu message |
284
|
|
|
$filler += (int)$pcs[6][0]; // get filler |
285
|
|
|
if ($num_seq == 1 // valid single message |
286
|
|
|
|| $num_seq == $pseq // valid multi-part message |
287
|
|
|
) { |
288
|
|
|
if ($num_seq != 1) { // test |
|
|
|
|
289
|
|
|
//echo $rawdata; |
290
|
|
|
} |
291
|
|
|
return $this->process_ais_itu($itu, strlen($itu), $filler, $aux /*, $ais_ch*/); |
292
|
|
|
} |
293
|
|
|
} // end process raw AIS string (checksum passed) |
294
|
|
|
} |
295
|
|
|
return -1; |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
// incoming data from serial or IP comms |
299
|
|
|
public function process_ais_buf($ibuf) { |
300
|
|
|
static $cbuf = ""; |
301
|
|
|
$cbuf = $cbuf.$ibuf; |
302
|
|
|
$last_pos = 0; |
303
|
|
|
$result = new stdClass(); |
304
|
|
|
while ( ($start = strpos($cbuf,"VDM",$last_pos)) !== FALSE) { |
305
|
|
|
//while ( ($start = strpos($cbuf,"!AI",$last_pos)) !== FALSE) { |
306
|
|
|
//DEBUG echo $cbuf; |
307
|
|
|
if ( ($end = strpos($cbuf,"\r\n", $start)) !== FALSE) { //TBD need to trim? |
308
|
|
|
$tst = substr($cbuf, $start - 3, ($end - $start + 3)); |
309
|
|
|
//DEBUG echo "[$start $end $tst]\n"; |
310
|
|
|
$result = $this->process_ais_raw( $tst, "" ); |
311
|
|
|
$last_pos = $end + 1; |
312
|
|
|
} else break; |
313
|
|
|
} |
314
|
|
|
if ($last_pos > 0) $cbuf = substr($cbuf, $last_pos); // move... |
315
|
|
|
if (strlen($cbuf) > 1024) $cbuf = ""; // prevent overflow simple mode... |
316
|
|
|
return $result; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
// incoming data from serial or IP comms |
320
|
|
|
public function process_ais_line($cbuf) { |
321
|
|
|
$result = new stdClass(); |
|
|
|
|
322
|
|
|
$start = strpos($cbuf,"VDM"); |
323
|
|
|
$tst = substr($cbuf, $start - 3); |
324
|
|
|
$result = $this->process_ais_raw( $tst, "" ); |
325
|
|
|
return $result; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/* AIS Encoding |
329
|
|
|
*/ |
330
|
|
|
private function mk_ais_lat( $lat ) { |
331
|
|
|
//$lat = 1.2569; |
332
|
|
|
if ($lat<0.0) { |
333
|
|
|
$lat = -$lat; |
334
|
|
|
$neg=true; |
335
|
|
|
} else $neg=false; |
336
|
|
|
$latd = 0x00000000; |
|
|
|
|
337
|
|
|
$latd = intval ($lat * 600000.0); |
338
|
|
|
if ($neg==true) { |
|
|
|
|
339
|
|
|
$latd = ~$latd; |
340
|
|
|
$latd+=1; |
341
|
|
|
$latd &= 0x07FFFFFF; |
342
|
|
|
} |
343
|
|
|
return $latd; |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
private function mk_ais_lon( $lon ) { |
347
|
|
|
//$lon = 103.851; |
348
|
|
|
if ($lon<0.0) { |
349
|
|
|
$lon = -$lon; |
350
|
|
|
$neg=true; |
351
|
|
|
} else $neg=false; |
352
|
|
|
$lond = 0x00000000; |
|
|
|
|
353
|
|
|
$lond = intval ($lon * 600000.0); |
354
|
|
|
if ($neg==true) { |
|
|
|
|
355
|
|
|
$lond = ~$lond; |
356
|
|
|
$lond+=1; |
357
|
|
|
$lond &= 0x0FFFFFFF; |
358
|
|
|
} |
359
|
|
|
return $lond; |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
private function char2bin($name, $max_len) { |
363
|
|
|
$len = strlen($name); |
364
|
|
|
if ($len > $max_len) $name = substr($name,0,$max_len); |
365
|
|
|
if ($len < $max_len) $pad = str_repeat('0', ($max_len - $len) * 6); |
366
|
|
|
else $pad = ''; |
367
|
|
|
$rv = ''; |
368
|
|
|
$ais_chars = array( |
369
|
|
|
'@'=>0, 'A'=>1, 'B'=>2, 'C'=>3, 'D'=>4, 'E'=>5, 'F'=>6, 'G'=>7, 'H'=>8, 'I'=>9, |
370
|
|
|
'J'=>10, 'K'=>11, 'L'=>12, 'M'=>13, 'N'=>14, 'O'=>15, 'P'=>16, 'Q'=>17, 'R'=>18, 'S'=>19, |
371
|
|
|
'T'=>20, 'U'=>21, 'V'=>22, 'W'=>23, 'X'=>24, 'Y'=>25, 'Z'=>26, '['=>27, '\\'=>28, ']'=>29, |
372
|
|
|
'^'=>30, '_'=>31, ' '=>32, '!'=>33, '\"'=>34, '#'=>35, '$'=>36, '%'=>37, '&'=>38, '\''=>39, |
373
|
|
|
'('=>40, ')'=>41, '*'=>42, '+'=>43, ','=>44, '-'=>45, '.'=>46, '/'=>47, '0'=>48, '1'=>49, |
374
|
|
|
'2'=>50, '3'=>51, '4'=>52, '5'=>53, '6'=>54, '7'=>55, '8'=>56, '9'=>57, ':'=>58, ';'=>59, |
375
|
|
|
'<'=>60, '='=>61, '>'=>62, '?'=>63 |
376
|
|
|
); |
377
|
|
|
$_a = str_split($name); |
378
|
|
|
if ($_a) foreach ($_a as $_1) { |
|
|
|
|
379
|
|
|
if (isset($ais_chars[$_1])) $dec = $ais_chars[$_1]; |
380
|
|
|
else $dec = 0; |
381
|
|
|
$bin = str_pad(decbin( $dec ), 6, '0', STR_PAD_LEFT); |
382
|
|
|
$rv .= $bin; |
383
|
|
|
//echo "$_1 $dec ($bin)<br/>"; |
384
|
|
|
} |
385
|
|
|
return $rv.$pad; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
private function mk_ais($_enc, $_part=1,$_total=1,$_seq='',$_ch='A') { |
389
|
|
|
$len_bit = strlen($_enc); |
390
|
|
|
$rem6 = $len_bit % 6; |
391
|
|
|
$pad6_len = 0; |
392
|
|
|
if ($rem6) $pad6_len = 6 - $rem6; |
393
|
|
|
//echo $pad6_len.'<br>'; |
394
|
|
|
$_enc .= str_repeat("0", $pad6_len); // pad the text... |
395
|
|
|
$len_enc = strlen($_enc) / 6; |
396
|
|
|
//echo $_enc.' '.$len_enc.'<br/>'; |
397
|
|
|
$itu = ''; |
398
|
|
|
for ($i=0; $i<$len_enc; $i++) { |
399
|
|
|
$offset = $i * 6; |
400
|
|
|
$dec = bindec(substr($_enc,$offset,6)); |
401
|
|
|
if ($dec < 40) $dec += 48; |
402
|
|
|
else $dec += 56; |
403
|
|
|
//echo chr($dec)." $dec<br/>"; |
404
|
|
|
$itu .= chr($dec); |
405
|
|
|
} |
406
|
|
|
// add checksum |
407
|
|
|
$chksum = 0; |
408
|
|
|
$itu = "AIVDM,$_part,$_total,$_seq,$_ch,".$itu.",0"; |
409
|
|
|
$len_itu = strlen($itu); |
410
|
|
|
for ($i=0; $i<$len_itu; $i++) { |
411
|
|
|
$chksum ^= ord( $itu[$i] ); |
412
|
|
|
} |
413
|
|
|
$hex_arr = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); |
414
|
|
|
$lsb = $chksum & 0x0F; |
415
|
|
|
if ($lsb >=0 && $lsb <= 15 ) $lsbc = $hex_arr[$lsb]; |
416
|
|
|
else $lsbc = '0'; |
417
|
|
|
$msb = (($chksum & 0xF0) >> 4) & 0x0F; |
418
|
|
|
if ($msb >=0 && $msb <= 15 ) $msbc = $hex_arr[$msb]; |
419
|
|
|
else $msbc = '0'; |
420
|
|
|
$itu = '!'.$itu."*{$msbc}{$lsbc}\r\n"; |
421
|
|
|
return $itu; |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
public function parse($buffer) { |
425
|
|
|
$data = $this->process_ais_buf($buffer); |
426
|
|
|
if (!is_object($data)) return array(); |
427
|
|
|
if ($data->lon != 0) $result['longitude'] = $data->lon; |
|
|
|
|
428
|
|
|
if ($data->lat != 0) $result['latitude'] = $data->lat; |
|
|
|
|
429
|
|
|
$result['ident'] = trim($data->name); |
430
|
|
|
$result['timestamp'] = $data->ts; |
431
|
|
|
$result['mmsi'] = $data->mmsi; |
432
|
|
|
if ($data->sog != -1.0) $result['speed'] = $data->sog; |
433
|
|
|
if ($data->cog != 0) $result['heading'] = $data->cog; |
434
|
|
|
/* |
435
|
|
|
$ro->cls = 0; // AIS class undefined, also indicate unparsed msg |
436
|
|
|
$ro->id = bindec(substr($_aisdata,0,6)); |
437
|
|
|
*/ |
438
|
|
|
return $result; |
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
public function parse_line($buffer) { |
442
|
|
|
$result = array(); |
443
|
|
|
$data = new stdClass(); |
|
|
|
|
444
|
|
|
$start = strpos($buffer,"VDM"); |
445
|
|
|
$tst = substr($buffer, $start - 3); |
446
|
|
|
$data = $this->process_ais_raw( $tst, "" ); |
447
|
|
|
if (!is_object($data)) return array(); |
448
|
|
|
if ($data->lon != 0) $result['longitude'] = $data->lon; |
449
|
|
|
if ($data->lat != 0) $result['latitude'] = $data->lat; |
450
|
|
|
$result['ident'] = trim(str_replace('@','',$data->name)); |
451
|
|
|
$result['timestamp'] = $data->ts; |
452
|
|
|
$result['mmsi'] = $data->mmsi; |
453
|
|
|
if ($data->sog != -1.0) $result['speed'] = $data->sog; |
454
|
|
|
if ($data->cog != 0) $result['heading'] = $data->cog; |
455
|
|
|
if ($data->status != '') $result['status'] = $data->status; |
456
|
|
|
$result['all'] = (array) $data; |
457
|
|
|
/* |
458
|
|
|
$ro->cls = 0; // AIS class undefined, also indicate unparsed msg |
459
|
|
|
$ro->id = bindec(substr($_aisdata,0,6)); |
460
|
|
|
*/ |
461
|
|
|
return $result; |
462
|
|
|
} |
463
|
|
|
} |
464
|
|
|
|
This error can happen if you refactor code and forget to move the variable initialization.
Let’s take a look at a simple example:
The above code is perfectly fine. Now imagine that we re-order the statements:
In that case,
$x
would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.