<syntaxhighlight lang="php">

  1. !/usr/bin/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">

  1. !/usr/bin/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>