<syntaxhighlight lang="php">

  1. !/usr/bin/php

<?php

  1. $master = "master.ham-dmr.be:62031"; // BM Master, this is the Belgian one.
  2. $repeater_id = 9991234; // Change this to your own ID
  3. $rptcall = "ON7LDS"; // Change this to your own call
  4. $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

  1. $p1=substr($line,0,$offset);
  2. $p2=substr($line,$offset);
  3. $p3="";
  4. while (strlen($p2)>0) {
  5. $p=substr($p2,0,4);
  6. $p4=unpack("N",$p);
  7. $p3.="(".$p4[1].")";
  8. $p2=substr($p2,4);
  9. }
  10. if ($inhex) {
  11. $p3=" ".dechex($p4[1]);
  12. }
  13. return $p1.$p3;

}

function sig_handler($signo) {

  1. switch ($signo) {
  2. case SIGTERM:
  3. // handle shutdown tasks
  4. echo "Caught SIGTERM...\n";
  5. exit;
  6. break;
  7. case SIGHUP:
  8. // handle restart tasks
  9. echo "Caught SIGHUP...\n";
  10. break;
  11. case SIGUSR1:
  12. echo "Caught SIGUSR1...\n";
  13. break;
  14. default:
  15. // handle all other signals
  16. echo "Caught $signo...\n";
  17. }

}


function Byte2Hex($zin) {

  1. $r="";
  2. for ($i=0; $i<strlen($zin); $i++) {
  3. $r.=sprintf("[%02X]",$zin[$i]);
  4. }
  5. return $r;

}

function decodeerDMR($data) {

  1. global $calls;
  1. if (strlen($data)>12) {
  2. $SEQ = ord($data[0]);
  3. $SID = unpack("N",chr(0).substr($data,1,3));
  4. $DST = unpack("N",chr(0).substr($data,4,3));
  5. $RID = unpack("N",substr($data,7,4));
  1. if (isset($calls[$SID[1]])) $Scall=$calls[$SID[1]][1]." ".$calls[$SID[1]][3]; else $Scall="?";
  2. if (isset($calls[$RID[1]])) $Rcall=$calls[$RID[1]][1]." ".$calls[$RID[1]][3]; else $Rcall="?";
  1. $res1 = sprintf("%03d Van %8d (%8s) --> %8d [ Via Repeater %10d (%8s) ] ",$SEQ,$SID[1],$Scall,$DST[1],$RID[1],$Rcall);
  1. $stat=ord($data[11]);
  1. /*
  2. FLCO_GROUP = 0,
  3. FLCO_USER_USER = 3,
  4. FLCO_TALKER_ALIAS_HEADER = 4,
  5. FLCO_TALKER_ALIAS_BLOCK1 = 5,
  6. FLCO_TALKER_ALIAS_BLOCK2 = 6,
  7. FLCO_TALKER_ALIAS_BLOCK3 = 7,
  8. FLCO_GPS_INFO = 8
  9. */
  1. if (($stat & 0x80)>0) $SLOT=2; else $SLOT=1;
  2. if (($stat & 0x40)>0) $FLCO="user"; else $FLCO="group";
  3. if (($stat & 0x20)>0) $dataSync=1; else $dataSync=0;
  4. if (($stat & 0x10)>0) $voiceSync=1; else $voiceSync=0;
  5. $res2 = sprintf(" ".
  6. "Slot %d, FLCO %s, datasync %d, voiceSync %d ",$SLOT,$FLCO,$dataSync,$voiceSync);
  7. if ($dataSync) {
  8. $datatype=$stat&0x0F;

// $data=substr($data,16); // $res2.=sprintf(" data type %02X (%s)",$datatype,bin2hex($data) );

  1. $res2.=sprintf(" data type %02X",$datatype );
  2. } else $datatype=0;
  1. if ($dataSync)
  1. return $res1."\n".$res2;
  2. } 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");


  1. $number = pack('N', $repeater_id);
  1. $handle = fsockopen("udp://$master");
  2. stream_set_timeout($handle, 10);
  1. $phase = 0;
  2. $message = "RPTL$number";
  3. fwrite($handle, $message);
  4. print(">> ".show($message,4)."\n");
  1. /* INPUT (in_Buffer):
  2. // 0000 0 000 001 1111 1 1111 222222222233333333334444444444555 5 5
  3. // 0123 4 567 890 1234 5 6789 012345678901234567890123456789012 3 4
  4. // DMRD SQ SID DST RID X StrID VData(33) BER RSSI
  1. // SQ seq. nr (just increases)
  2. // SID source ID
  3. // DST dest. ID
  4. // RID repeater ID
  5. // X slot : call type : frame type : data type : voice seq
  6. // StrID stream ID (same while PTT pressed)
  7. // DMR data
  1. : bitpattern : SCFFDDDD
  2. S = Slot 0 Slot 1
  3. 1 Slot 2
  4. C = Call type 0 group call
  5. 1 unit to unit call
  6. FF = Frame type 0x00 voice
  7. 0x01 voice sync
  8. 0x10 data sync
  9. 0x11 unused
  10. DDDD = data type / voice seq
  11. When data sync, this is the Data Type from the Slot Type
  12. When voice/voice sync this is the voice sequence no, with 0 equal to A in the DMR specification, 1 for B, etc


  1. a1 = 1 0 10 0001 Slot 1, group call, data sync, data type 1
  2. 90 = 1 0 01 0000 Slot 1, group call, voice sync, A
  3. 81 = 1 0 00 0001 Slot 1, group call, voice , B
  4. 82 = 1 0 00 0010 Slot 1, group call, voice , C
  5. 83 = 1 0 00 0011 Slot 1, group call, voice , D
  6. 84 = 1 0 00 0100 Slot 1, group call, voice , E
  7. 85 = 1 0 00 0101 Slot 1, group call, voice , F
  8. a2 = 1 0 10 0010 Slot 1, unit to unit, data sync, data type 2


  1. while (true)
  2. {
  3. $buffer = fread($handle, 200);
  1. if (strlen($buffer) == 0)
  2. {
  3. $message = "RPTPING$number";
  4. fwrite($handle, $message);
  5. print(">> PING : ".show($message,7)."\n");
  6. continue;
  7. }
  8.  
  9. if (substr($buffer, 0, 7) == "MSTPONG")
  10. {
  11. $data = substr($buffer, 7);
  12. print("<< PONG : " . show($message,7) . "\n");
  13. continue;
  14. }
  15.  
  16. if (substr($buffer, 0, 7) == "RPTSBKN")
  17. {
  18. $data = show($buffer, 7);
  19. print("<< Beacon : " . bin2hex($data) . "\n");
  20. continue;
  21. }
  22.  
  23. if (substr($buffer, 0, 7) == "RPTRSSI")
  24. {
  25. $data = show($buffer, 7);
  26. print("<< RSSI : " . bin2hex($data) . "\n");
  27. continue;
  28. }


  1. if (substr($buffer, 0, 4) == "DMRD")
  2. {
  3. $data = substr($buffer, 4);
  4. // print("<< DMR Data : " . bin2hex($data) . "\n");
  5. $dezedata=bin2hex(substr($data,1,11));
  6. if (strcmp($dezedata,$vorigedata)!=0)
  7. print(" " . decodeerDMR($data) . "\n");
  8. else {
  9. // print(" " . "[$vorigedata]\n");
  10. // print(" " . "[$dezedata]\n");
  11. }
  12. $vorigedata=$dezedata;
  13.  
  14. print("<< DMR Data : " . bin2hex(substr($data,0,11))." ".bin2hex(substr($data,11)) . "\n");
  15. continue;
  16. }
  17.  
  18. if (strpos($buffer, "ACK")!==false)
  19. { $p=strpos($buffer, "ACK")+3; print("<< ".show($buffer,$p,true)."\n"); }
  20. else
  21. { print("<< $buffer\n"); print("Unexpected response - END\n"); exit(1); }
  22.  
  23.  
  24. if ((substr($buffer, 0, 6) == "RPTACK") && ($phase==0) &&
  25. (strlen($buffer) == 10))
  26. {
  27. $phase=1;
  28. $salt = substr($buffer, 6, 4);
  29. $salthex=show($salt,0,true);
  30. $digest = hash("sha256", "$salt$password", true); //binair
  31. $digesthex = hash("sha256", "$salt$password"); //hexits
  32. print("Salt: $salthex Digest: $digesthex\n");
  33.  
  34. $message = "RPTK$number$digest";
  35. fwrite($handle, $message);
  36. print(">> RPTK".Show($message,0,true)."\n");
  37. continue;
  38. }
  39.  
  40. if ((substr($buffer, 0, 6) == "RPTACK") && ($phase==1) &&
  41. (strlen($buffer) == 10) )
  42. {
  43. $phase = 2;
  44. $message = "RPTC";
  45. $message .= $number; // ID of the repeater
  46. $message .= sprintf("%-8s", $rptcall); // Name of the repeater
  47. $message .= "000000000"; // RX (9 char, but we are just software)
  48. $message .= "000000000"; // TX (9 char, but we are just software)
  49. $message .= "01"; // Power (2 char)
  50. $message .= "01"; // Color Code (2 char)
  51. $message .= sprintf("%-09f", 0); // Latitude
  52. $message .= sprintf("%-08f", 0); // Longitude
  53. $message .= "000"; // Height (3 char)
  54. $message .= sprintf("%-20s", "Mobile"); // Location
  55. $message .= sprintf("%-19s", "PHP Test program"); //Description
  56. $message .= "4"; // Slots (0, 1=1, 2=2, 1&2=3, 4=simplex)
  57. $message .= sprintf("%-124s", "http://on7lds.net"); //URL
  58. $message .= sprintf("%-40s", "20170301-php-test-program"); // Software ID
  59. $message .= sprintf("%-40s", "MMDVM_PHP_Program"); // Package ID
  60.  
  61. $length = strlen($message);
  62. print("Repeater configuration: $length bytes\n");
  63.  
  64. fwrite($handle, $message);
  65. // print(">> ".Bin2Hex($message)."\n");
  66. print(">> ".$message."\n");
  67. }
  68.  
  69.  
  70. if ((substr($buffer, 0, 6) == "MSTNAK") &&
  71. (strlen($buffer) == 14) &&
  72. ($state == 1))
  73. {
  74. $phase = 0;
  75. $message = "RPTL$number";
  76. fwrite($handle, $message);
  77. print(">> RPTL $message\n");
  78. }
  79. }
  1. fclose($handle);
  2. ?>
  3. </syntaxhighlight>