Line 171: | Line 171: | ||
a2 = 1 0 10 0010 Slot 1, unit to unit, data sync, data type 2 | a2 = 1 0 10 0010 Slot 1, unit to unit, data sync, data type 2 | ||
− | + | ||
while (true) | while (true) | ||
Line 177: | Line 177: | ||
$buffer = fread($handle, 200); | $buffer = fread($handle, 200); | ||
− | + | if (strlen($buffer) == 0) | |
− | + | { | |
− | + | $message = "RPTPING$number"; | |
− | + | fwrite($handle, $message); | |
− | + | print(">> PING : ".show($message,7)."\n"); | |
− | + | continue; | |
− | + | } | |
+ | |||
+ | if (substr($buffer, 0, 7) == "MSTPONG") | ||
+ | { | ||
+ | $data = substr($buffer, 7); | ||
+ | print("<< PONG : " . show($message,7) . "\n"); | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | if (substr($buffer, 0, 7) == "RPTSBKN") | ||
+ | { | ||
+ | $data = show($buffer, 7); | ||
+ | print("<< Beacon : " . bin2hex($data) . "\n"); | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | if (substr($buffer, 0, 7) == "RPTRSSI") | ||
+ | { | ||
+ | $data = show($buffer, 7); | ||
+ | print("<< RSSI : " . bin2hex($data) . "\n"); | ||
+ | continue; | ||
+ | } | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | if (substr($buffer, 0, 4) == "DMRD") | |
− | + | { | |
− | + | $data = substr($buffer, 4); | |
− | + | // print("<< DMR Data : " . bin2hex($data) . "\n"); | |
− | + | $dezedata=bin2hex(substr($data,1,11)); | |
− | + | if (strcmp($dezedata,$vorigedata)!=0) | |
− | + | print(" " . decodeerDMR($data) . "\n"); | |
− | + | else { | |
− | + | // print(" " . "[$vorigedata]\n"); | |
− | + | // print(" " . "[$dezedata]\n"); | |
− | + | } | |
− | + | $vorigedata=$dezedata; | |
− | // print("<< DMR Data : " . bin2hex($data) . "\n"); | + | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | // print(" " . "[$vorigedata]\n"); | + | |
− | // print(" " . "[$dezedata]\n"); | + | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | print("<< DMR Data : " . bin2hex(substr($data,0,11))." ".bin2hex(substr($data,11)) . "\n"); | |
− | + | continue; | |
− | + | } | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | if (strpos($buffer, "ACK")!==false) | |
− | + | { $p=strpos($buffer, "ACK")+3; print("<< ".show($buffer,$p,true)."\n"); } | |
− | + | else | |
− | + | { print("<< $buffer\n"); print("Unexpected response - END\n"); exit(1); } | |
− | + | ||
− | + | ||
− | + | if ((substr($buffer, 0, 6) == "RPTACK") && ($phase==0) && | |
− | + | (strlen($buffer) == 10)) | |
− | + | { | |
− | + | $phase=1; | |
− | + | $salt = substr($buffer, 6, 4); | |
− | + | $salthex=show($salt,0,true); | |
− | + | $digest = hash("sha256", "$salt$password", true); //binair | |
− | + | $digesthex = hash("sha256", "$salt$password"); //hexits | |
− | + | print("Salt: $salthex Digest: $digesthex\n"); | |
− | + | ||
− | + | $message = "RPTK$number$digest"; | |
− | + | fwrite($handle, $message); | |
− | + | print(">> RPTK".Show($message,0,true)."\n"); | |
− | + | continue; | |
− | + | } | |
− | // print(">> ".Bin2Hex($message)."\n"); | + | |
− | + | if ((substr($buffer, 0, 6) == "RPTACK") && ($phase==1) && | |
− | + | (strlen($buffer) == 10) ) | |
− | + | { | |
− | + | $phase = 2; | |
− | + | ||
− | + | $message = "RPTC"; | |
− | + | $message .= $number; // ID of the repeater | |
− | + | $message .= sprintf("%-8s", $rptcall); // Name of the repeater | |
− | + | $message .= "000000000"; // RX (9 char, but we are just software) | |
− | + | $message .= "000000000"; // TX (9 char, but we are just software) | |
− | + | $message .= "01"; // Power (2 char) | |
− | + | $message .= "01"; // Color Code (2 char) | |
− | + | $message .= sprintf("%-09f", 0); // Latitude | |
− | + | $message .= sprintf("%-08f", 0); // Longitude | |
+ | $message .= "000"; // Height (3 char) | ||
+ | $message .= sprintf("%-20s", "Mobile"); // Location | ||
+ | $message .= sprintf("%-19s", "PHP Test program"); //Description | ||
+ | $message .= "4"; // Slots (0, 1=1, 2=2, 1&2=3, 4=simplex) | ||
+ | $message .= sprintf("%-124s", "http://on7lds.net"); //URL | ||
+ | $message .= sprintf("%-40s", "20170301-php-test-program"); // Software ID | ||
+ | $message .= sprintf("%-40s", "MMDVM_PHP_Program"); // Package ID | ||
+ | |||
+ | $length = strlen($message); | ||
+ | print("Repeater configuration: $length bytes\n"); | ||
+ | |||
+ | fwrite($handle, $message); | ||
+ | // print(">> ".Bin2Hex($message)."\n"); | ||
+ | print(">> ".$message."\n"); | ||
+ | } | ||
+ | |||
+ | |||
+ | if ((substr($buffer, 0, 6) == "MSTNAK") && | ||
+ | (strlen($buffer) == 14) && | ||
+ | ($state == 1)) | ||
+ | { | ||
+ | $phase = 0; | ||
+ | $message = "RPTL$number"; | ||
+ | fwrite($handle, $message); | ||
+ | print(">> RPTL $message\n"); | ||
+ | } | ||
+ | } | ||
− | + | fclose($handle); | |
− | ?> | + | ?> |
− | </syntaxhighlight> | + | </syntaxhighlight> |
<syntaxhighlight lang="php">
<?php
$master = "master.ham-dmr.be:62031"; // BM Master, this is the Belgian one. $repeater_id = 9991234; // Change this to your own ID $rptcall = "ON7LDS"; // Change this to your own call $password = "passw0rd"; // This needs to match the password in the master
// tick use required as of PHP 4.3.0
declare(ticks = 1);
function show($line,$offset,$inhex=false) { //split string in asci part and (decoded) binary part
$p1=substr($line,0,$offset); $p2=substr($line,$offset); $p3=""; while (strlen($p2)>0) { $p=substr($p2,0,4); $p4=unpack("N",$p); $p3.="(".$p4[1].")"; $p2=substr($p2,4); } if ($inhex) { $p3=" ".dechex($p4[1]); } return $p1.$p3;
}
function sig_handler($signo) {
switch ($signo) { case SIGTERM: // handle shutdown tasks echo "Caught SIGTERM...\n"; exit; break; case SIGHUP: // handle restart tasks echo "Caught SIGHUP...\n"; break; case SIGUSR1: echo "Caught SIGUSR1...\n"; break; default: // handle all other signals echo "Caught $signo...\n"; }
}
function Byte2Hex($zin) {
$r=""; for ($i=0; $i<strlen($zin); $i++) { $r.=sprintf("[%02X]",$zin[$i]); } return $r;
}
function decodeerDMR($data) {
global $calls;
if (strlen($data)>12) { $SEQ = ord($data[0]); $SID = unpack("N",chr(0).substr($data,1,3)); $DST = unpack("N",chr(0).substr($data,4,3)); $RID = unpack("N",substr($data,7,4));
if (isset($calls[$SID[1]])) $Scall=$calls[$SID[1]][1]." ".$calls[$SID[1]][3]; else $Scall="?"; if (isset($calls[$RID[1]])) $Rcall=$calls[$RID[1]][1]." ".$calls[$RID[1]][3]; else $Rcall="?";
$res1 = sprintf("%03d Van %8d (%8s) --> %8d [ Via Repeater %10d (%8s) ] ",$SEQ,$SID[1],$Scall,$DST[1],$RID[1],$Rcall);
$stat=ord($data[11]);
/* FLCO_GROUP = 0, FLCO_USER_USER = 3, FLCO_TALKER_ALIAS_HEADER = 4, FLCO_TALKER_ALIAS_BLOCK1 = 5, FLCO_TALKER_ALIAS_BLOCK2 = 6, FLCO_TALKER_ALIAS_BLOCK3 = 7, FLCO_GPS_INFO = 8 */
if (($stat & 0x80)>0) $SLOT=2; else $SLOT=1; if (($stat & 0x40)>0) $FLCO="user"; else $FLCO="group"; if (($stat & 0x20)>0) $dataSync=1; else $dataSync=0; if (($stat & 0x10)>0) $voiceSync=1; else $voiceSync=0; $res2 = sprintf(" ". "Slot %d, FLCO %s, datasync %d, voiceSync %d ",$SLOT,$FLCO,$dataSync,$voiceSync); if ($dataSync) { $datatype=$stat&0x0F;
// $data=substr($data,16); // $res2.=sprintf(" data type %02X (%s)",$datatype,bin2hex($data) );
$res2.=sprintf(" data type %02X",$datatype ); } else $datatype=0;
if ($dataSync)
return $res1."\n".$res2; } else return "?";
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Program start ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
$dezedata=""; $vorigedata="";
// Get calls from file for DMRId -> call conversion $calls=array(); $file = fopen("custom.csv","r"); while(! feof($file)) { $call=fgetcsv($file); $calls[$call[0]]=$call; } fclose($file);
// setup signal handlers pcntl_signal(SIGTERM, "sig_handler"); pcntl_signal(SIGHUP, "sig_handler"); pcntl_signal(SIGUSR1, "sig_handler");
$number = pack('N', $repeater_id);
$handle = fsockopen("udp://$master"); stream_set_timeout($handle, 10);
$phase = 0; $message = "RPTL$number"; fwrite($handle, $message); print(">> ".show($message,4)."\n");
/* INPUT (in_Buffer): // 0000 0 000 001 1111 1 1111 222222222233333333334444444444555 5 5 // 0123 4 567 890 1234 5 6789 012345678901234567890123456789012 3 4 // DMRD SQ SID DST RID X StrID VData(33) BER RSSI
// SQ seq. nr (just increases) // SID source ID // DST dest. ID // RID repeater ID // X slot : call type : frame type : data type : voice seq // StrID stream ID (same while PTT pressed) // DMR data
X : bitpattern : SCFFDDDD S = Slot 0 Slot 1 1 Slot 2 C = Call type 0 group call 1 unit to unit call FF = Frame type 0x00 voice 0x01 voice sync 0x10 data sync 0x11 unused DDDD = data type / voice seq When data sync, this is the Data Type from the Slot Type When voice/voice sync this is the voice sequence no, with 0 equal to A in the DMR specification, 1 for B, etc
a1 = 1 0 10 0001 Slot 1, group call, data sync, data type 1 90 = 1 0 01 0000 Slot 1, group call, voice sync, A 81 = 1 0 00 0001 Slot 1, group call, voice , B 82 = 1 0 00 0010 Slot 1, group call, voice , C 83 = 1 0 00 0011 Slot 1, group call, voice , D 84 = 1 0 00 0100 Slot 1, group call, voice , E 85 = 1 0 00 0101 Slot 1, group call, voice , F a2 = 1 0 10 0010 Slot 1, unit to unit, data sync, data type 2
while (true) { $buffer = fread($handle, 200);
if (strlen($buffer) == 0) { $message = "RPTPING$number"; fwrite($handle, $message); print(">> PING : ".show($message,7)."\n"); continue; } if (substr($buffer, 0, 7) == "MSTPONG") { $data = substr($buffer, 7); print("<< PONG : " . show($message,7) . "\n"); continue; } if (substr($buffer, 0, 7) == "RPTSBKN") { $data = show($buffer, 7); print("<< Beacon : " . bin2hex($data) . "\n"); continue; } if (substr($buffer, 0, 7) == "RPTRSSI") { $data = show($buffer, 7); print("<< RSSI : " . bin2hex($data) . "\n"); continue; }
if (substr($buffer, 0, 4) == "DMRD") { $data = substr($buffer, 4); // print("<< DMR Data : " . bin2hex($data) . "\n"); $dezedata=bin2hex(substr($data,1,11)); if (strcmp($dezedata,$vorigedata)!=0) print(" " . decodeerDMR($data) . "\n"); else { // print(" " . "[$vorigedata]\n"); // print(" " . "[$dezedata]\n"); } $vorigedata=$dezedata; print("<< DMR Data : " . bin2hex(substr($data,0,11))." ".bin2hex(substr($data,11)) . "\n"); continue; } if (strpos($buffer, "ACK")!==false) { $p=strpos($buffer, "ACK")+3; print("<< ".show($buffer,$p,true)."\n"); } else { print("<< $buffer\n"); print("Unexpected response - END\n"); exit(1); } if ((substr($buffer, 0, 6) == "RPTACK") && ($phase==0) && (strlen($buffer) == 10)) { $phase=1; $salt = substr($buffer, 6, 4); $salthex=show($salt,0,true); $digest = hash("sha256", "$salt$password", true); //binair $digesthex = hash("sha256", "$salt$password"); //hexits print("Salt: $salthex Digest: $digesthex\n"); $message = "RPTK$number$digest"; fwrite($handle, $message); print(">> RPTK".Show($message,0,true)."\n"); continue; } if ((substr($buffer, 0, 6) == "RPTACK") && ($phase==1) && (strlen($buffer) == 10) ) { $phase = 2; $message = "RPTC"; $message .= $number; // ID of the repeater $message .= sprintf("%-8s", $rptcall); // Name of the repeater $message .= "000000000"; // RX (9 char, but we are just software) $message .= "000000000"; // TX (9 char, but we are just software) $message .= "01"; // Power (2 char) $message .= "01"; // Color Code (2 char) $message .= sprintf("%-09f", 0); // Latitude $message .= sprintf("%-08f", 0); // Longitude $message .= "000"; // Height (3 char) $message .= sprintf("%-20s", "Mobile"); // Location $message .= sprintf("%-19s", "PHP Test program"); //Description $message .= "4"; // Slots (0, 1=1, 2=2, 1&2=3, 4=simplex) $message .= sprintf("%-124s", "http://on7lds.net"); //URL $message .= sprintf("%-40s", "20170301-php-test-program"); // Software ID $message .= sprintf("%-40s", "MMDVM_PHP_Program"); // Package ID $length = strlen($message); print("Repeater configuration: $length bytes\n"); fwrite($handle, $message); // print(">> ".Bin2Hex($message)."\n"); print(">> ".$message."\n"); } if ((substr($buffer, 0, 6) == "MSTNAK") && (strlen($buffer) == 14) && ($state == 1)) { $phase = 0; $message = "RPTL$number"; fwrite($handle, $message); print(">> RPTL $message\n"); } }
fclose($handle); ?> </syntaxhighlight>
<syntaxhighlight lang="php">
<?php
$master = "master.ham-dmr.be:62031"; // BM Master, this is the Belgian one. $repeater_id = 9991234; // Change this to your own ID $rptcall = "ON7LDS"; // Change this to your own call $password = "passw0rd"; // This needs to match the password in the master
// tick use required as of PHP 4.3.0
declare(ticks = 1);
function show($line,$offset,$inhex=false) { //split string in asci part and (decoded) binary part
$p1=substr($line,0,$offset); $p2=substr($line,$offset); $p3=""; while (strlen($p2)>0) { $p=substr($p2,0,4); $p4=unpack("N",$p); $p3.="(".$p4[1].")"; $p2=substr($p2,4); } if ($inhex) { $p3=" ".dechex($p4[1]); } return $p1.$p3;
}
function sig_handler($signo) {
switch ($signo) { case SIGTERM: // handle shutdown tasks echo "Caught SIGTERM...\n"; exit; break; case SIGHUP: // handle restart tasks echo "Caught SIGHUP...\n"; break; case SIGUSR1: echo "Caught SIGUSR1...\n"; break; default: // handle all other signals echo "Caught $signo...\n"; }
}
function Byte2Hex($zin) {
$r=""; for ($i=0; $i<strlen($zin); $i++) { $r.=sprintf("[%02X]",$zin[$i]); } return $r;
}
function decodeerDMR($data) {
global $calls;
if (strlen($data)>12) { $SEQ = ord($data[0]); $SID = unpack("N",chr(0).substr($data,1,3)); $DST = unpack("N",chr(0).substr($data,4,3)); $RID = unpack("N",substr($data,7,4));
if (isset($calls[$SID[1]])) $Scall=$calls[$SID[1]][1]." ".$calls[$SID[1]][3]; else $Scall="?"; if (isset($calls[$RID[1]])) $Rcall=$calls[$RID[1]][1]." ".$calls[$RID[1]][3]; else $Rcall="?";
$res1 = sprintf("%03d Van %8d (%8s) --> %8d [ Via Repeater %10d (%8s) ] ",$SEQ,$SID[1],$Scall,$DST[1],$RID[1],$Rcall);
$stat=ord($data[11]);
/* FLCO_GROUP = 0, FLCO_USER_USER = 3, FLCO_TALKER_ALIAS_HEADER = 4, FLCO_TALKER_ALIAS_BLOCK1 = 5, FLCO_TALKER_ALIAS_BLOCK2 = 6, FLCO_TALKER_ALIAS_BLOCK3 = 7, FLCO_GPS_INFO = 8 */
if (($stat & 0x80)>0) $SLOT=2; else $SLOT=1; if (($stat & 0x40)>0) $FLCO="user"; else $FLCO="group"; if (($stat & 0x20)>0) $dataSync=1; else $dataSync=0; if (($stat & 0x10)>0) $voiceSync=1; else $voiceSync=0; $res2 = sprintf(" ". "Slot %d, FLCO %s, datasync %d, voiceSync %d ",$SLOT,$FLCO,$dataSync,$voiceSync); if ($dataSync) { $datatype=$stat&0x0F;
// $data=substr($data,16); // $res2.=sprintf(" data type %02X (%s)",$datatype,bin2hex($data) );
$res2.=sprintf(" data type %02X",$datatype ); } else $datatype=0;
if ($dataSync)
return $res1."\n".$res2; } else return "?";
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Program start ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
$dezedata=""; $vorigedata="";
// Get calls from file for DMRId -> call conversion $calls=array(); $file = fopen("custom.csv","r"); while(! feof($file)) { $call=fgetcsv($file); $calls[$call[0]]=$call; } fclose($file);
// setup signal handlers pcntl_signal(SIGTERM, "sig_handler"); pcntl_signal(SIGHUP, "sig_handler"); pcntl_signal(SIGUSR1, "sig_handler");
$number = pack('N', $repeater_id);
$handle = fsockopen("udp://$master"); stream_set_timeout($handle, 10);
$phase = 0; $message = "RPTL$number"; fwrite($handle, $message); print(">> ".show($message,4)."\n");
/* INPUT (in_Buffer): // 0000 0 000 001 1111 1 1111 222222222233333333334444444444555 5 5 // 0123 4 567 890 1234 5 6789 012345678901234567890123456789012 3 4 // DMRD SQ SID DST RID X StrID VData(33) BER RSSI
// SQ seq. nr (just increases) // SID source ID // DST dest. ID // RID repeater ID // X slot : call type : frame type : data type : voice seq // StrID stream ID (same while PTT pressed) // DMR data
X : bitpattern : SCFFDDDD S = Slot 0 Slot 1 1 Slot 2 C = Call type 0 group call 1 unit to unit call FF = Frame type 0x00 voice 0x01 voice sync 0x10 data sync 0x11 unused DDDD = data type / voice seq When data sync, this is the Data Type from the Slot Type When voice/voice sync this is the voice sequence no, with 0 equal to A in the DMR specification, 1 for B, etc
a1 = 1 0 10 0001 Slot 1, group call, data sync, data type 1 90 = 1 0 01 0000 Slot 1, group call, voice sync, A 81 = 1 0 00 0001 Slot 1, group call, voice , B 82 = 1 0 00 0010 Slot 1, group call, voice , C 83 = 1 0 00 0011 Slot 1, group call, voice , D 84 = 1 0 00 0100 Slot 1, group call, voice , E 85 = 1 0 00 0101 Slot 1, group call, voice , F a2 = 1 0 10 0010 Slot 1, unit to unit, data sync, data type 2
*/
while (true) { $buffer = fread($handle, 200);
if (strlen($buffer) == 0) { $message = "RPTPING$number"; fwrite($handle, $message); print(">> PING : ".show($message,7)."\n"); continue; }
if (substr($buffer, 0, 7) == "MSTPONG") { $data = substr($buffer, 7); print("<< PONG : " . show($message,7) . "\n"); continue; }
if (substr($buffer, 0, 7) == "RPTSBKN") { $data = show($buffer, 7); print("<< Beacon : " . bin2hex($data) . "\n"); continue; }
if (substr($buffer, 0, 7) == "RPTRSSI") { $data = show($buffer, 7); print("<< RSSI : " . bin2hex($data) . "\n"); continue; }
if (substr($buffer, 0, 4) == "DMRD") { $data = substr($buffer, 4); // print("<< DMR Data : " . bin2hex($data) . "\n"); $dezedata=bin2hex(substr($data,1,11)); if (strcmp($dezedata,$vorigedata)!=0) print(" " . decodeerDMR($data) . "\n"); else { // print(" " . "[$vorigedata]\n"); // print(" " . "[$dezedata]\n"); } $vorigedata=$dezedata;
print("<< DMR Data : " . bin2hex(substr($data,0,11))." ".bin2hex(substr($data,11)) . "\n"); continue; }
if (strpos($buffer, "ACK")!==false) { $p=strpos($buffer, "ACK")+3; print("<< ".show($buffer,$p,true)."\n"); } else { print("<< $buffer\n"); print("Unexpected response - END\n"); exit(1); }
if ((substr($buffer, 0, 6) == "RPTACK") && ($phase==0) &&
(strlen($buffer) == 10))
{
$phase=1;
$salt = substr($buffer, 6, 4);
$salthex=show($salt,0,true);
$digest = hash("sha256", "$salt$password", true); //binair
$digesthex = hash("sha256", "$salt$password"); //hexits
print("Salt: $salthex Digest: $digesthex\n");
$message = "RPTK$number$digest"; fwrite($handle, $message); print(">> RPTK".Show($message,0,true)."\n"); continue; }
if ((substr($buffer, 0, 6) == "RPTACK") && ($phase==1) && (strlen($buffer) == 10) ) { $phase = 2;
$message = "RPTC"; $message .= $number; // ID of the repeater $message .= sprintf("%-8s", $rptcall); // Name of the repeater $message .= "000000000"; // RX (9 char, but we are just software) $message .= "000000000"; // TX (9 char, but we are just software) $message .= "01"; // Power (2 char) $message .= "01"; // Color Code (2 char) $message .= sprintf("%-09f", 0); // Latitude $message .= sprintf("%-08f", 0); // Longitude $message .= "000"; // Height (3 char) $message .= sprintf("%-20s", "Mobile"); // Location $message .= sprintf("%-19s", "PHP Test program"); //Description $message .= "4"; // Slots (0, 1=1, 2=2, 1&2=3, 4=simplex) $message .= sprintf("%-124s", "http://on7lds.net"); //URL $message .= sprintf("%-40s", "20170301-php-test-program"); // Software ID $message .= sprintf("%-40s", "MMDVM_PHP_Program"); // Package ID
$length = strlen($message); print("Repeater configuration: $length bytes\n");
fwrite($handle, $message); // print(">> ".Bin2Hex($message)."\n"); print(">> ".$message."\n"); }
if ((substr($buffer, 0, 6) == "MSTNAK") &&
(strlen($buffer) == 14) &&
($state == 1))
{
$phase = 0;
$message = "RPTL$number";
fwrite($handle, $message);
print(">> RPTL $message\n");
}
}
fclose($handle);
?> </syntaxhighlight>