[
  {
    "path": ".classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<classpath>\r\n\t<classpathentry kind=\"src\" path=\"src\"/>\r\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER\"/>\r\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.junit.JUNIT_CONTAINER/4\"/>\r\n\t<classpathentry kind=\"output\" path=\"bin\"/>\r\n</classpath>\r\n"
  },
  {
    "path": ".project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<projectDescription>\r\n\t<name>DMRDecode</name>\r\n\t<comment></comment>\r\n\t<projects>\r\n\t</projects>\r\n\t<buildSpec>\r\n\t\t<buildCommand>\r\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\r\n\t\t\t<arguments>\r\n\t\t\t</arguments>\r\n\t\t</buildCommand>\r\n\t</buildSpec>\r\n\t<natures>\r\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\r\n\t</natures>\r\n</projectDescription>\r\n"
  },
  {
    "path": "README.txt",
    "content": "PLEASE NOTE : This project is obsolete and no longer supported. It has been superseded by other better DMR decoders but I'm leaving the repository on here as people seem to be interested.\r\n\r\n###\r\n\r\nThe aim of this project is to provide a user friendly DMR data decoder for hobbyists.\r\nIt is Java based so should run under Microsoft Windows , Apple and Linux PC's. The\r\nonly hardware needed will be a radio scanner with a discriminator audio output.\r\n\r\nA binary version of this program which will run without being compiled is available from ..\r\n\r\nhttps://drive.google.com/file/d/0B48eZhmoMv5nZUlpdTRNamNrQkk/view?usp=share_link&resourcekey=0-e1snv0OJnYG3bPY8_TfsDg\r\n\r\nThis was however compiled for an earlier version of Java so may or may not still run.\r\n\r\nThe core of the program is based around the open source DSD program by an unknown\r\nauthor. This program was written in C and runs under Linux only so my first job was to\r\nconvert that code to Java and remove all sections that don't relate to DMR.\r\n\r\nMilestone 1 - To detect voice and data sync words reached (3rd September 2010)\r\n\r\nMilestone 2 - To reliably detect voice and data sync words. (Reached 15th October 2010)\r\n\r\nMilestone 3 - To decode the CACH TACT (Reached 22nd October 2010)\r\n\r\nMilestone 3.5 (!) - To decode the SLOT TYPE PDU (Reached 4th November 2010)\r\n\r\nMilestone 4 - To decode the Short LCs that make up the CACH payload. (Reached 25th November 2010)\r\n\r\nMilestone 5 - To work out why and then fix the problem that is causing single bit errors in frames. (Reached 14th February 2011)\r\n\r\nMilestone 6 - Add a feature where all DMR users are logged. (Reached 19th February 2011)\r\n\r\nMilestone 7 - To fix a memory leak type problem where the heap memory used by the program keeps growing until it slows down and crashes. (Reached 28th February 2011)\r\n\r\nMilestone 8 - To pass certain data on to other programs via TCP/IP socket. (Reached 1st March 2011)\r\n\r\nBuild 48 - Convert the Connect Plus site ID field in SLCO 10 packets from 3 bits to 8 bits\r\n           Add the Utilities class to remove duplicated code.\r\n           Display the manufacturers name in Proprietary Data Headers.\r\n           Start adding build change details in this README file.\r\n           \r\nBuild 49 - Display the payload in half rate data packets both as binary and ASCII.\r\n\r\nBuild 50 - Correctly display an 8 bit Site ID in Connect Plus SLCO 9 packets.\r\n\r\nBuild 51 - Display Privacy Header PDUs as raw binary\r\n\r\nBuild 52 - Fix a bug which meant that PI Header PDUs in embedded frames weren't being displayed\r\n\r\nBuild 53 - Allows the users settings to be saved in DMRDecode_settings.xml and reloaded on start up.\r\n\r\nBuild 54 - Adds a CPU utilization fix contributed by Chris Sams\r\n\r\nBuild 55 - Adds an option to not view voice frames and also replaces all instances of StringBuffer\r\n           with StringBuilder\r\n           \r\nBuild 56 - Adds the date to the quick log.\r\n           Displays the data filters at the start of each log and shows any filter changes in the log file.\r\n           \r\nFrom build 57 onwards Java 7 is required to run DMRDecode.\r\n           \r\nBuild 57 - Decode Connect Plus CSBKO 01 FID=6 PDUs\r\n\r\nBuild 58 - Add a link to the new download page. Also decode Rate ¾ Data Continuation PDUs as raw binary.\r\n\r\nBuild 59 - Display the Time Slot in Connect Plus Channel Grants (CSBKO 03 FID=6). This information was kindly\r\n           provided by W8EMX on the Radioreference forums. \r\n           \r\nBuild 60 - Allows the user to select their audio source.\r\n\r\nBuild 61 - Now saves the selected audio source in the settings file\r\n\r\nBuild 62 - Disable the capture and debug menu items. Also change some code so virtual audio cables can be selected.\r\n\r\nBuild 63 - Grey out the capture and debug items. Catch errors when the audio source is set from the settings file and\r\n           better display errors when changing audio sources.\r\n           \r\nBuild 64 - Ensure that only sound capture devices can be selected when choosing a mixer. Also better display information\r\n           from any errors during mixer selection.\r\n           \r\nBuild 65 - Saves half rate to data to debug.csv for later analysis\r\n\r\nBuild 68 - Decodes rate 3/4 data and has a unified method of handling data.\r\n\r\nBuild 69 - Improve the decoding of Capacity Plus CSBKO=62 PDUs. This information was kindly\r\n           provided by Eric Cottrell on the Radioreference forums. \r\n           Add the Linkedin menu item.\r\n           \r\nBuild 70 - Fix a bug in the big_m_csbko62() method.\r\n\r\nBuild 71 - Change so that if a quick log file exists ask the user if they want to overwrite it or append to the existing file.\r\n           Change so that if log file exists ask the user if they want to overwrite it or append data to the existing file.\r\n      \t   Add support for CSBKO=31 FID=16 (Call Alert) and CSBKO=32 FID=16 (Call Alert Ack) this information kindly provided \r\n      \t   by bben95 on the Radioreference forums.\r\n      \t   Decode Tier III Sys_Parms Short LCs\r\n      \t   \r\nBuild 72 - Fix bugs in the csbko31fid16() and csbko32fid16() methods were the to and from idents were transposed. \r\n\r\nBuild 73 - Add basic support for monitoring MS and Direct mode.\r\n\r\nBuild 74 - The program now supports nearly all Tier III CSBKOs\r\n      \r\nIan Wraith (12th November 2022)\r\n\r\n\r\n"
  },
  {
    "path": "pom.xml",
    "content": "<project>\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>com.dmr</groupId>\n    <artifactId>DRMDecode</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n    <properties>\n        <project.java.version>1.8</project.java.version>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <maven.compiler.target>1.8</maven.compiler.target>\n    </properties>\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-jar-plugin</artifactId>\n                <configuration>\n                    <archive>\n                        <manifest>\n                            <addClasspath>false</addClasspath>\n                            <mainClass>com.dmr.DMRDecode</mainClass>\n                        </manifest>\n                    </archive>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n    <dependencies>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.10</version>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "src/main/java/com/dmr/AudioInThread.java",
    "content": "package com.dmr;\n\nimport javax.swing.JOptionPane;\nimport java.io.DataOutputStream;\nimport java.io.PipedOutputStream;\n\npublic class AudioInThread implements Runnable {\n\tprivate boolean run;\n\tprivate boolean audioReady;\n\tprivate boolean gettingAudio;\n\tprivate static int VOLUMEBUFFERSIZE=100;\n\tprivate int volumeBuffer[]=new int[VOLUMEBUFFERSIZE];\n\tprivate int volumeBufferCounter=0;\n\tprivate static int ISIZE=256;\n\tprivate byte buffer[]=new byte[ISIZE+1];\n\tprivate PipedOutputStream ps=new PipedOutputStream();\n\tprivate DataOutputStream outPipe=new DataOutputStream(ps);\n\tprivate AudioMixer audioMixer;\n\t\n\t\n\t// Filter details ..\n\t// filtertype\t =\t Raised Cosine\n\t// samplerate\t =\t 48000\n\t// corner\t =\t 2400\n\t// beta\t =\t 0.2 \n\t// impulselen\t =\t 81\n\t// racos\t =\t sqrt\n\t// comp\t =\t no\n\t// bits\t =\t\n\t// logmin\t =\t\n\tprivate static int NZEROS=80;\n\tprivate double xv[]=new double [NZEROS+1];\n\tprivate int xvCounter=0;\n\t// 0.2 //\n\tprivate static double GAIN=9.868410946e+00;\n\tprivate static double XCOEFFS[]=\n\t  {+0.0273676736, +0.0190682959, +0.0070661879, -0.0075385898,\n\t    -0.0231737159, -0.0379433607, -0.0498333862, -0.0569528373,\n\t    -0.0577853377, -0.0514204905, -0.0377352004, -0.0174982391,\n\t    +0.0076217868, +0.0351552125, +0.0620353691, +0.0848941519,\n\t    +0.1004237235, +0.1057694293, +0.0989127431, +0.0790009892,\n\t    +0.0465831968, +0.0037187043, -0.0460635022, -0.0979622825,\n\t    -0.1462501260, -0.1847425896, -0.2073523972, -0.2086782295,\n\t    -0.1845719273, -0.1326270847, -0.0525370892, +0.0537187153,\n\t    +0.1818868577, +0.3256572849, +0.4770745929, +0.6271117870,\n\t    +0.7663588857, +0.8857664963, +0.9773779594, +1.0349835419,\n\t    +1.0546365475, +1.0349835419, +0.9773779594, +0.8857664963,\n\t    +0.7663588857, +0.6271117870, +0.4770745929, +0.3256572849,\n\t    +0.1818868577, +0.0537187153, -0.0525370892, -0.1326270847,\n\t    -0.1845719273, -0.2086782295, -0.2073523972, -0.1847425896,\n\t    -0.1462501260, -0.0979622825, -0.0460635022, +0.0037187043,\n\t    +0.0465831968, +0.0790009892, +0.0989127431, +0.1057694293,\n\t    +0.1004237235, +0.0848941519, +0.0620353691, +0.0351552125,\n\t    +0.0076217868, -0.0174982391, -0.0377352004, -0.0514204905,\n\t    -0.0577853377, -0.0569528373, -0.0498333862, -0.0379433607,\n\t    -0.0231737159, -0.0075385898, +0.0070661879, +0.0190682959,\n\t    +0.0273676736};\n\t\n\n    public AudioInThread (DMRDecode theApp) {\n    \taudioMixer=new AudioMixer();\n    \trun=false;\n    \tgettingAudio=false;\n    \tsetupAudio();\n      }\n    \n    // Main\n    public void run()\t{\n    \t// Run continiously\n    \tfor (;;)\t{\n    \t\t// If the audio device is ready , the program wants to and we aren't already then\n    \t\t// get data from the audio device.\n    \t\tif ((audioReady==true)&&(run==true)&&(gettingAudio==false)) getSample();\n     \t}\n    }\n    \n    private void setupAudio()\t{\n    \ttry\t{\n    \t\taudioMixer.setDefaultLine();\n\t\t\taudioMixer.openLine();\n\t\t\taudioMixer.line.start();\n\t\t\taudioReady=true;\n    \t}\n    \tcatch (Exception e)\t{\n    \t\taudioReady=false;\n    \t\tString err=\"setupAudio() : \"+e.toString();\n    \t\tJOptionPane.showMessageDialog(null,err,\"DMRDecode\",JOptionPane.ERROR_MESSAGE);\n    \t}\n    }\n\t \n    // Read in 2 bytes from the audio source combine them together into a single integer\n    // then write that into the sound buffer\n    private void getSample ()\t{\n    \t// Tell the main thread we getting audio\n    \tgettingAudio=true;\n    \tint a,sample,count,total=0;\n    \t// READ in ISIZE bytes and convert them into ISIZE/2 integers\n    \t// Doing it this way reduces CPU loading\n\t\ttry\t{\n\t\t\t\twhile (total<ISIZE)\t{\n\t\t\t\t\tcount=audioMixer.line.read(buffer,0,ISIZE);\n\t\t\t\t\ttotal=total+count;\n\t\t\t  \t\t}\n\t\t\t  \t} catch (Exception e)\t{\n\t\t\t  \t\tString err=e.getMessage();\n\t\t\t  \t\tJOptionPane.showMessageDialog(null,err,\"DMRDecode\",JOptionPane.ERROR_MESSAGE);\n\t\t\t  \t}\n\t\t// Get the required number of samples\n\t\tfor (a=0;a<ISIZE;a=a+2)\t{\n\t\t\tsample=(buffer[a]<<8)+buffer[a+1];\n\t\t\t// Add this sample to the circular volume buffer\n\t\t\taddToVolumeBuffer(sample);\n\t\t\ttry\t{\n\t\t\t\t// Put this through a root raised filter\n\t\t\t\t// and then put the result into the output pipe\n\t\t\t\toutPipe.writeInt(rootRaisedFilter(sample));\n            }\n            catch (Exception e)\t{\n                String err=e.getMessage();\n                JOptionPane.showMessageDialog(null,err,\"DMRDecode\",JOptionPane.ERROR_MESSAGE);\n            }\n\t\t}\n\t\t// The the main thread we have stopped fetching audio\n\t\tgettingAudio=false;\t\n    }\n    \n    \n    // Called when the main program wants to start receiving audio\n    public void startAudio ()\t{\n    \trun=true;\n    }\n    \n    // Getter tells the main program if the audio device is ready\n    public boolean getAudioReady ()\t{\n    \treturn audioReady;\n    }\n    \n    // When called this closes the audio device\n    public void shutDownAudio ()\t{\n    \trun=false;\n    \taudioMixer.line.close();\n    }\n    \n    // A root raised cosine pulse shaping filter\n    public int rootRaisedFilter (int sample)\t{\n    \tint i;\n    \tdouble sum=0.0;\n    \tdouble in=(double)sample;\n    \t// Add the latest sample to the xv circular buffer\n    \txv[xvCounter]=in/GAIN;\n    \t// Increment the circular buffer counter and zero it if needed\n    \txvCounter++;\n    \tif (xvCounter==(NZEROS+1)) xvCounter=0;\n    \t// Do the RRC maths taking account of the fact that XV is a circular buffer\n    \tint xvShadow=xvCounter;\n    \tfor (i=0;i<=NZEROS;i++)\t{\n    \t\tsum=sum+(XCOEFFS[i]*xv[xvShadow]);\n    \t\txvShadow++;\n    \t\tif (xvShadow==(NZEROS+1)) xvShadow=0;\n    \t}\n    \t// All done\n    \treturn (int)sum;\n    }\n\n    \n    // Add this sample to the circular volume buffer\n    private void addToVolumeBuffer (int tsample)\t{\n    \tvolumeBuffer[volumeBufferCounter]=tsample;\n    \tvolumeBufferCounter++;\n    \tif (volumeBufferCounter==VOLUMEBUFFERSIZE) volumeBufferCounter=0;\n    }\n    \n    // Return the average volume over the last VOLUMEBUFFERSIZE samples\n    public int returnVolumeAverage ()\t{\n    \tlong va=0;\n    \tint a,volumeAverage=0;\n    \tfor (a=0;a<VOLUMEBUFFERSIZE;a++)\t{\n    \t\tva=va+Math.abs(volumeBuffer[a]);\n    \t}\n    \tvolumeAverage=(int)va/VOLUMEBUFFERSIZE;\t\n    \treturn volumeAverage;\n    }\n    \n    // Return the PipedOutputSteam object so it can be connected to\n    public PipedOutputStream getPipedWriter() {\n        return ps;\n      }\n    \n\tpublic boolean changeMixer(String mixerName)\t{\n\t\treturn audioMixer.changeMixer(mixerName);\n\t}   \n    \n\tpublic String getMixerName()\t{\n\t\treturn audioMixer.getMixer().getMixerInfo().getName();\n\t}\n\t\n\tpublic String getMixerErrorMessage() {\n\t\treturn audioMixer.getErrorMsg();\n\t}\n    \n}\n"
  },
  {
    "path": "src/main/java/com/dmr/AudioMixer.java",
    "content": "package com.dmr;\n\nimport javax.sound.sampled.AudioFormat;\nimport javax.sound.sampled.AudioSystem;\nimport javax.sound.sampled.DataLine;\nimport javax.sound.sampled.Line;\nimport javax.sound.sampled.LineUnavailableException;\nimport javax.sound.sampled.Mixer;\nimport javax.sound.sampled.TargetDataLine;\n\n/**\n * @author Andy\n * A wrapper class for switching between audio inputs (mixers)\n *\n */\nclass AudioMixer{\n\tpublic String description;\n\tpublic Mixer mixer;\n\tpublic TargetDataLine line;\n\tpublic Line.Info lineInfo;\n\tpublic AudioFormat format = null;\n\tprivate String errorMsg;\n\t\n\tpublic AudioMixer(){\n\t\tformat=setAudioFormat();\t\t\n\t}\n\t\n\tpublic AudioMixer(DMRDecode theApp, String x, Mixer m, Line.Info l){\n\t\t//this.theApp = theApp;\n\t\tthis.description = x;\n\t\tthis.mixer = m;\n\t\tthis.lineInfo = l;\n\t\tformat = setAudioFormat();\n\t}\n\t\n\t/**\n\t * Setters and getters\n\t */\n\tpublic Mixer getMixer() {\n\t\treturn mixer;\n\t}\n\n\tpublic void setMixer(Mixer mixer) {\n\t\tthis.mixer = mixer;\n\t}\n\t\n\tpublic TargetDataLine getLine() {\n\t\treturn line;\n\t}\n\n\tpublic void setLine(TargetDataLine line) {\n\t\tthis.line = line;\n\t}\n\t\n\t/**\n\t * Set the audio format required\n\t * @return\n\t */\n\tprivate AudioFormat setAudioFormat(){\n\t\t// Sample at 48000 Hz , 16 bit samples , 1 channel , signed with bigendian numbers\n\t\treturn new AudioFormat(48000,16,1,true,true);\n\t}\n\t\n\t/**\n\t * Set the default line for the default mixer\n\t */\n\tpublic void setDefaultLine(){\n\t    Mixer mx = AudioSystem.getMixer(null);\t//default mixer\n\t    this.setMixer(mx);\n\t\tDataLine.Info info = getDataLineInfo();\n\t\t\n\t\ttry{\n\t\t\tthis.line = (TargetDataLine) AudioSystem.getLine(info);\n\t\t}catch(LineUnavailableException ex){\n\t\t\tSystem.out.println(\"Line Unavailable\");\n\t\t}\n\t}\n\t\n\t\n\t/**\n\t * Get the DataLine.info object for the TargetDataLine \n\t * @return\n\t */\n\tprivate DataLine.Info getDataLineInfo(){\n\t\tDataLine.Info info = new DataLine.Info(TargetDataLine.class, this.format); // format is an AudioFormat object\n\t\tif (!AudioSystem.isLineSupported(info)) {\n\t\t    // Handle the error.\n\t\t\tSystem.out.println(\"Error in AudioSystem\");\n\t\t}\n\t\t\n\t\treturn info;\n\t}\n\t\n\t/**\n\t * Gets a data line for the specified mixer\n\t * @param mix\n\t * @return\n\t */\n\tpublic Line getDataLineForMixer(){\n\t\tTargetDataLine line = null;\n\t\ttry {\n\t\t\tline = (TargetDataLine) this.mixer.getLine(getDataLineInfo());\n\t\t} catch (LineUnavailableException e) {\n\t\t\tSystem.out.println(\"Error getting mix line:\" + e.getMessage());\n\t\t}\n\t\t\n\t\treturn line;\n\t}\n\t\n\t/**\n\t * Open the current line\n\t */\n\tpublic void openLine(){\n\t\ttry {\n\t\t\tthis.line.open(format);\n\t\t} catch (LineUnavailableException e) {\n\t\t\tSystem.out.println(\"Unable to open line:\" + e.getMessage());\n\t\t}\n\t}\n\t\n\t/**\n\t * Change the mixer and restart the TargetDataLine\n\t * @param mixerName\n\t */\n\tpublic boolean changeMixer(String mixerName) {\n\t\tMixer mx=null;\n\t\ttry\t{\n\t\t\t//stop current line\n\t\t\tthis.line.stop();\n\t\t\tthis.line.close();\n\t\t\tthis.line.flush();\n\t\t\t//set the new mixer and line\n\t\t\tmx=AudioSystem.getMixer(getMixerInfo(mixerName));\n\t\t\tthis.setMixer(mx);\n\t\t\tthis.line=(TargetDataLine) getDataLineForMixer();\n\t\t\t//restart\n\t\t\topenLine();\n\t\t\tthis.line.start();\n\t\t}\n\t\tcatch (Exception e)\t{\n\t\t\t// Record the exception\n\t\t\terrorMsg=e.getMessage();\n\t\t\t// then if a mixer has been obtained then display some information about it\n\t\t\tif (mx!=null)\t{\n\t\t\t\tMixer.Info mInfo=mx.getMixerInfo();\n\t\t\t\terrorMsg=errorMsg+\"\\nMixer Name : \"+mInfo.getName()+\"\\nMixer Description : \"+mInfo.getDescription();\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t/**\n\t * Get the MixerInfo based on the mixer name\n\t * @param mixerName\n\t * @return\n\t */\n\tpublic Mixer.Info getMixerInfo(String mixerName){\n\t\tMixer.Info mixers[]=AudioSystem.getMixerInfo();\n\t\t//iterate the mixers and display TargetLines\n\t\tfor (int i=0; i< mixers.length; i++){\n\t\t\tMixer m=AudioSystem.getMixer(mixers[i]);\n\t\t\t// Ensure that only sound capture devices can be selected\n\t\t\tboolean isCaptureDevice=m.getMixerInfo().getDescription().endsWith(\"Capture\");\n\t\t\tif ((m.getMixerInfo().getName().equals(mixerName))&&(isCaptureDevice==true)){\n\t\t\t\treturn m.getMixerInfo();\n\t\t\t}\n\t\t}\n\t\t//if no mixer found, returns null which is the default mixer on the machine\n\t\treturn null;\n\t}\n\n\t// Return any error message\n\tpublic String getErrorMsg() {\n\t\treturn errorMsg;\n\t}\n\n}\n\n"
  },
  {
    "path": "src/main/java/com/dmr/BPTC19696.java",
    "content": "package com.dmr;\n\npublic class BPTC19696 {\n\tprivate boolean rawData[]=new boolean[196];\n\tprivate boolean deInterData[]=new boolean[196];\n\tprivate boolean outData[]=new boolean[96];\n\t\n\t// The main decode function\n\tpublic boolean decode (byte[] dibit_buf)\t{\n\t\t// Get the raw binary\n\t\textractBinary(dibit_buf);\n\t\t// Deinterleave\n\t\tdeInterleave();\n\t\t// Error check\n\t\tif (errorCheck()==true)\t{\n\t\t\t// Extract Data\n\t\t\textractData();\n\t\t\treturn true;\n\t\t}\n\t\telse return false;\n\t}\n\t\n\t// Extract the binary from the dibit data\n\tprivate void extractBinary (byte[] dibit_buf)\t{\n\t\tint a,r=0;\n\t\t// First block\n\t\tfor (a=12;a<61;a++)\t{\n\t\t\tif (dibit_buf[a]==0)\t{\n\t\t\t\trawData[r]=false;\n\t\t\t\trawData[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==1)\t{\n\t\t\t\trawData[r]=false;\n\t\t\t\trawData[r+1]=true;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==2)\t{\n\t\t\t\trawData[r]=true;\n\t\t\t\trawData[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==3)\t{\n\t\t\t\trawData[r]=true;\n\t\t\t\trawData[r+1]=true;\n\t\t\t}\n\t\t\tr=r+2;\n\t\t}\n\t\t// Second block\n\t\tfor (a=95;a<144;a++)\t{\n\t\t\tif (dibit_buf[a]==0)\t{\n\t\t\t\trawData[r]=false;\n\t\t\t\trawData[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==1)\t{\n\t\t\t\trawData[r]=false;\n\t\t\t\trawData[r+1]=true;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==2)\t{\n\t\t\t\trawData[r]=true;\n\t\t\t\trawData[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==3)\t{\n\t\t\t\trawData[r]=true;\n\t\t\t\trawData[r+1]=true;\n\t\t\t}\n\t\t\tr=r+2;\n\t\t}\n\t}\n\t\n\t// Deinterleave the raw data\n\tprivate void deInterleave ()\t{\n\t\tint a,interleaveSequence;\n\t\t// The first bit is R(3) which is not used so can be ignored\n\t\tfor (a=0;a<196;a++)\t{\n\t\t\t// Calculate the interleave sequence\n\t\t\tinterleaveSequence=(a*181)%196;\n\t\t\t// Shuffle the data\n\t\t\tdeInterData[a]=rawData[interleaveSequence];\n\t\t}\n\t}\n\t\n\t// Check each row with a Hamming (15,11,3) code\n\t// Return false if there is a problem\n\tprivate boolean errorCheck ()\t{\n\t\tint a,r,c,pos;\n\t\tboolean row[]=new boolean[15];\n\t\tboolean col[]=new boolean[13];\n\t\t// Run through each of the 9 rows containing data\n\t\tfor (r=0;r<9;r++)\t{\n\t\t\tpos=(r*15)+1;\n\t\t\tfor (a=0;a<15;a++)\t{\n\t\t\t\trow[a]=deInterData[pos];\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\tif (hamming15113(row)==false) return false;\n\t\t}\n\t\t// Run through each of the 15 columns\n\t\tfor (c=0;c<15;c++)\t{\n\t\t\tpos=c+1;\n\t\t\tfor (a=0;a<13;a++){\n\t\t\t\tcol[a]=deInterData[pos];\n\t\t\t\tpos=pos+15;\n\t\t\t}\n\t\t\tif (hamming1393(col)==false) return false;\n\t\t}\n\t\t\n\treturn true;\n\t}\n\t\n\t// Hamming (15,11,3) check a boolean data array\n\tprivate boolean hamming15113 (boolean d[])\t{\n\t\tboolean c[]=new boolean[4];\n\t\t// Calculate the checksum this row should have\n\t\tc[0]=d[0]^d[1]^d[2]^d[3]^d[5]^d[7]^d[8];\n\t\tc[1]=d[1]^d[2]^d[3]^d[4]^d[6]^d[8]^d[9];\n\t\tc[2]=d[2]^d[3]^d[4]^d[5]^d[7]^d[9]^d[10];\n\t\tc[3]=d[0]^d[1]^d[2]^d[4]^d[6]^d[7]^d[10];\n\t\t// Compare these with the actual bits\n\t\tif ((c[0]==d[11])&&(c[1]==d[12])&&(c[2]==d[13])&&(c[3]==d[14])) return true;\n\t\telse return false;\n\t}\n\t\n\t// Hamming (13,9,3) check a boolean data array\n\tprivate boolean hamming1393 (boolean d[])\t{\n\t\tboolean c[]=new boolean[4];\n\t\t// Calculate the checksum this column should have\n\t\tc[0]=d[0]^d[1]^d[3]^d[5]^d[6];\n\t\tc[1]=d[0]^d[1]^d[2]^d[4]^d[6]^d[7];\n\t\tc[2]=d[0]^d[1]^d[2]^d[3]^d[5]^d[7]^d[8];\n\t\tc[3]=d[0]^d[2]^d[4]^d[5]^d[8];\n\t\t// Compare these with the actual bits\n\t\tif ((c[0]==d[9])&&(c[1]==d[10])&&(c[2]==d[11])&&(c[3]==d[12])) return true;\n\t\telse return false;\n\t}\n\t\n\t// Extract the 96 bits of payload\n\tprivate void extractData()\t{\n\t\tint a,pos=0;\n\t\tfor (a=4;a<=11;a++)\t{\n\t\t\toutData[pos]=deInterData[a];\n\t\t\tpos++;\n\t\t}\n\t\tfor (a=16;a<=26;a++)\t{\n\t\t\toutData[pos]=deInterData[a];\n\t\t\tpos++;\n\t\t}\n\t\tfor (a=31;a<=41;a++)\t{\n\t\t\toutData[pos]=deInterData[a];\n\t\t\tpos++;\n\t\t}\n\t\tfor (a=46;a<=56;a++)\t{\n\t\t\toutData[pos]=deInterData[a];\n\t\t\tpos++;\n\t\t}\n\t\tfor (a=61;a<=71;a++)\t{\n\t\t\toutData[pos]=deInterData[a];\n\t\t\tpos++;\n\t\t}\n\t\tfor (a=76;a<=86;a++)\t{\n\t\t\toutData[pos]=deInterData[a];\n\t\t\tpos++;\n\t\t}\n\t\tfor (a=91;a<=101;a++)\t{\n\t\t\toutData[pos]=deInterData[a];\n\t\t\tpos++;\n\t\t}\n\t\tfor (a=106;a<=116;a++)\t{\n\t\t\toutData[pos]=deInterData[a];\n\t\t\tpos++;\n\t\t}\n\t\tfor (a=121;a<=131;a++)\t{\n\t\t\toutData[pos]=deInterData[a];\n\t\t\tpos++;\n\t\t}\n\t}\n\t\n\t// Pass the data output on\n\tpublic boolean[] dataOut()\t{\n\t\treturn outData;\n\t}\n\t\n\t// Extract the raw binary as output without doing anything else to it\n\tpublic boolean[] rawOut(byte[] di_buf)\t{\n\t\textractBinary(di_buf);\n\t\treturn rawData;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/BareBonesBrowserLaunch.java",
    "content": "package com.dmr;\n\nimport java.lang.reflect.Method;\nimport javax.swing.JOptionPane;\n\npublic class BareBonesBrowserLaunch {\n\tprivate static final String errMsg = \"Error attempting to launch web browser\";\n\n\tpublic static void openURL(String url) {\n\t\tString osName = System.getProperty(\"os.name\");\n\t\ttry {\n\t\t\tif (osName.startsWith(\"Mac OS\")) {\n\t\t\t\tClass<?> fileMgr = Class.forName(\"com.apple.eio.FileManager\");\n\t\t\t\tMethod openURL = fileMgr.getDeclaredMethod(\"openURL\", new Class[] { String.class });\n\t\t\t\topenURL.invoke(null, new Object[] { url });\n\t\t\t\t\n\t\t\t} else if (osName.startsWith(\"Windows\")){\n\t\t\t\tRuntime.getRuntime().exec( \"rundll32 url.dll,FileProtocolHandler \" + url);\n\t\t\t\t\n\t\t\t}else { // assume Unix or Linux\n\t\t\t\tString[] browsers = { \"firefox\", \"opera\", \"konqueror\", \"epiphany\", \"mozilla\", \"netscape\" };\n\t\t\t\tString browser = null;\n\t\t\t\tfor (int count = 0; count < browsers.length && browser == null; count++){\n\t\t\t\t\tif (Runtime.getRuntime().exec(\n\t\t\t\t\t\t\tnew String[] { \"which\", browsers[count] })\n\t\t\t\t\t\t\t.waitFor() == 0){\n\t\t\t\t\t\tbrowser = browsers[count];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (browser == null)\n\t\t\t\t\tthrow new Exception(\"Could not find web browser\");\n\t\t\t\telse\n\t\t\t\t\tRuntime.getRuntime().exec(new String[] { browser, url });\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tJOptionPane.showMessageDialog(null, errMsg + \":\\n\"\n\t\t\t\t\t+ e.getLocalizedMessage());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/CSBK.java",
    "content": "package com.dmr;\n\npublic class CSBK {\n\tboolean lb,pf;\n\tprivate String display[]=new String[3];\n\t\n\t// The main decode method\n\tpublic String[] decode (DMRDecode theApp,boolean bits[]) \t{\n\t\tint csbko,fid;\n\t\t// LB\n\t\tlb=bits[0];\n\t\t// PF\n\t\tpf=bits[1];\n\t\t// CSBKO\n\t\tif (bits[2]==true) csbko=32;\n\t\telse csbko=0;\n\t\tif (bits[3]==true) csbko=csbko+16;\n\t\tif (bits[4]==true) csbko=csbko+8;\n\t\tif (bits[5]==true) csbko=csbko+4;\n\t\tif (bits[6]==true) csbko=csbko+2;\n\t\tif (bits[7]==true) csbko++;\n\t\t// FID\n\t\tif (bits[8]==true) fid=128;\n\t\telse fid=0;\n\t\tif (bits[9]==true) fid=fid+64;\n\t\tif (bits[10]==true) fid=fid+32;\n\t\tif (bits[11]==true) fid=fid+16;\n\t\tif (bits[12]==true) fid=fid+8;\n\t\tif (bits[13]==true) fid=fid+4;\n\t\tif (bits[14]==true) fid=fid+2;\n\t\tif (bits[15]==true) fid++;\n\t\t// CSBK Types\n\t\t// 56 - BS_Dwn_Act\n\t\tif (csbko==56)\t{\n\t\t\tbs_dwn_act(bits);\n\t\t}\n\t\t// 01 (FID 6) - Connect Plus\n\t\telse if ((csbko==1)&&(fid==6))\t{\n\t\t\tbig_m_csbko01(theApp,bits);\n\t\t}\n\t\t// 03 (FID 6) - Connect Plus\n\t\telse if ((csbko==3)&&(fid==6))\t{\n\t\t\tbig_m_csbko03(theApp,bits);\n\t\t}\n\t\t// 04 - UU_V_Reg\n\t\telse if (csbko==4)\t{\n\t\t\tuu_v_reg(bits);\n\t\t}\n\t\t// 05 - UU_Ans_Rsp\n\t\telse if (csbko==5)\t{\n\t\t\tuu_ans_rep(bits);\n\t\t}\n\t\t// 25 (FID 00) - C_ALOHA (Tier III)\n\t\telse if ((csbko==25)&&(fid==0))\t{\n\t\t\tcsbko25fid0(theApp,bits);\n\t\t}\n\t\t// TODO : Support CSBKO 26 (FID 00) - C_UDTHD (Tier III) PDU defined in ETSI TS 102 361-4 V1.5.1 (2013-02) but not as a CSBK !\n\t\t// TODO : Support CSBKO 27 (FID 00) - C_UDTHU (Tier III) PDU defined in ETSI TS 102 361-4 V1.5.1 (2013-02) but not as a CSBK !\n\t\t// 28 (FID 00) - AHOY (Tier III)\n\t\telse if ((csbko==28)&&(fid==0))\t{\n\t\t\tcsbko28fid0(theApp,bits);\n\t\t}\t\n\t\t// 30 (FID 00) - C_ACKVIT (Tier III)\n\t\telse if ((csbko==30)&&(fid==0))\t{\n\t\t\tcsbko30fid0(theApp,bits);\n\t\t}\t\t\t\n\t\t// 31 (FID 00) - C_RAND (Tier III)\n\t\telse if ((csbko==31)&&(fid==0))\t{\n\t\t\tcsbko31fid0(theApp,bits);\n\t\t}\t\t\n\t\t// 31 (FID 16) - Call Alert \n\t\telse if ((csbko==31)&&(fid==16))\t{\n\t\t\tcsbko31fid16(theApp,bits);\n\t\t}\n\t\t// 32 (FID 00) - C_ACKD (Tier III)\n\t\telse if ((csbko==32)&&(fid==0))\t{\n\t\t\tcsbko32fid0(theApp,bits);\n\t\t}\n\t\t// 32 (FID 16) - Call Alert Ack\n\t\telse if ((csbko==32)&&(fid==16))\t{\n\t\t\tcsbko32fid16(theApp,bits);\n\t\t}\n\t\t// 33 (FID 00) - C_ACKU (Tier III)\n\t\telse if ((csbko==33)&&(fid==0))\t{\n\t\t\tcsbko33fid0(theApp,bits);\n\t\t}\t\t\n\t\t// TODO : Support CSBKO 34 (FID 00) - P_ACKD (Tier III)\tPDU not defined in ETSI TS 102 361-4 V1.5.1 (2013-02)\t\t\n\t\t// TODO : Support CSBKO 35 (FID 00) - P_ACKU (Tier III) PDU not defined in ETSI TS 102 361-4 V1.5.1 (2013-02)\t\t\t\n\t\t// 36 (FID 16) - Radio Check\n\t\telse if ((csbko==36)&&(fid==16))\t{\n\t\t\tcsbko36fid16(theApp,bits);\n\t\t}\n\t\t// 38 - NACK_Rsp\n\t\telse if (csbko==38)\t{\n\t\t\tnack_rsp(bits);\n\t\t}\n\t\t// 40 (FID 00) - C_BCAST (Tier III)\n\t\telse if ((csbko==40)&&(fid==0))\t{\n\t\t\tcsbko40fid0(theApp,bits);\n\t\t}\n\t\t// 42 (FID 00) - P_MAINT (Tier III)\n\t\telse if ((csbko==42)&&(fid==0))\t{\n\t\t\tcsbko42fid0(theApp,bits);\n\t\t}\t\t\n\t\t// 46 (FID 00) - P_CLEAR (Tier III)\n\t\telse if ((csbko==46)&&(fid==0))\t{\n\t\t\tcsbko46fid0(theApp,bits);\n\t\t}\n\t\t// 47 (FID 00) - P_PROTECT (Tier III)\n\t\telse if ((csbko==47)&&(fid==0))\t{\n\t\t\tcsbko47fid0(theApp,bits);\n\t\t}\t\t\n\t\t// 48 (FID 00) - PV_GRANT (Tier III)\n\t\telse if ((csbko==48)&&(fid==0))\t{\n\t\t\tcsbko48fid0(theApp,bits);\n\t\t}\n\t\t// 49 (FID 00) - TV_GRANT (Tier III)\n\t\telse if ((csbko==49)&&(fid==0))\t{\n\t\t\tcsbko49fid0(theApp,bits);\n\t\t}\t\n\t\t// 50 (FID 00) - BTV_GRANT (Tier III)\n\t\telse if ((csbko==50)&&(fid==0))\t{\n\t\t\tcsbko50fid0(theApp,bits);\n\t\t}\t\t\n\t\t// 51 (FID 00) - PD_GRANT (Tier III)\n\t\telse if ((csbko==51)&&(fid==0))\t{\n\t\t\tcsbko51fid0(theApp,bits);\n\t\t}\t\n\t\t// 52 (FID 00) - TD_GRANT (Tier III)\n\t\telse if ((csbko==52)&&(fid==0))\t{\n\t\t\tcsbko52fid0(theApp,bits);\n\t\t}\t\n\t\t// 57 (FID 00) - C_MOVE (Tier III)\n\t\telse if ((csbko==57)&&(fid==0))\t{\n\t\t\tcsbko57fid0(theApp,bits);\n\t\t}\t\t\t\n\t\t// 59 - Capacity Plus\n\t\telse if ((csbko==59)&&(fid==16))\t{\n\t\t\tbig_m_csbko59(theApp,bits);\n\t\t}\t\n\t\t// 61 - Pre_CSBK\n\t\telse if (csbko==61)\t{\n\t\t\tpreCSBK(theApp,bits);\n\t\t}\n\t\t// 62 - Capacity Plus\n\t\telse if (csbko==62)\t{\n\t\t\tbig_m_csbko62(theApp,bits);\n\t\t}\t\n\t\telse\t{\n\t\t\tunknownCSBK(csbko,fid,bits);\n\t\t}\n\t\treturn display;\n\t}\n\t\n\t// Handle unknown CSBK types\n\tprivate void unknownCSBK (int csbko,int fid,boolean bits[])\t{\n\t\tint a;\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tsb.append(\"Unknown CSBK : CSBKO=\"+Integer.toString(csbko)+\" + FID=\"+Integer.toString(fid)+\" \");\n\t\t// Display the binary\n\t\tfor (a=16;a<80;a++)\t{\n\t\t\tif (bits[a]==true) sb.append(\"1\");\n\t\t\telse sb.append(\"0\");\n\t\t}\n\t\tdisplay[0]=sb.toString();\n\t}\n\t\n\t// Handle a Preamble CSBK\n\tprivate void preCSBK (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tStringBuilder sc=new StringBuilder(250);\n\t\tUtilities utils=new Utilities();\n\t\t// 0 if CSBK , 1 if Data\n\t\tboolean dc=bits[16];\n\t\t// 0 if target is individual and 1 if group\n\t\tboolean gi=bits[17];\n\t\t// Bits 18,19,20,21,22,23 are reserved\n\t\t// Next 8 bits are the bits to follow\n\t\tint bfol=0;\n\t\tif (bits[24]==true) bfol=128;\n\t\tif (bits[25]==true) bfol=bfol+64;\n\t\tif (bits[26]==true) bfol=bfol+32;\n\t\tif (bits[27]==true) bfol=bfol+16;\n\t\tif (bits[28]==true) bfol=bfol+8;\n\t\tif (bits[29]==true) bfol=bfol+4;\n\t\tif (bits[30]==true) bfol=bfol+2;\n\t\tif (bits[31]==true) bfol++;\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,56);\n\t\t// Display this\n\t\tsb.append(\"Preamble CSBK : \");\n\t\tif (dc==false) sb.append(\" CSBK content \");\n\t\telse sb.append(\" Data content \");\n\t\tsb.append(Integer.toString(bfol)+\" Blocks to follow\");\n\t\tdisplay[0]=sb.toString();\t\t\n\t\tsc.append(\"Target Address : \"+Integer.toString(target));\n\t\tif (gi==true) sc.append(\" (Group)\");\n\t\tsc.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[1]=sc.toString();\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\n\t\tindex=theApp.usersLogged.findUserIndex(target);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsDataUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,theApp.currentChannel);\n\t\t}\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t\tindex=theApp.usersLogged.findUserIndex(source);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsDataUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,theApp.currentChannel);\n\t\t}\n\t}\n\t\t\n\t// BS Outbound Activation CSBK\n\tprivate void bs_dwn_act (boolean bits[])\t{\n\t\t// TODO : Full decoding of bs_dwn_act\n\t\tdisplay[0]=\"BS Outbound Activation\";\n\t}\n\t\n\t// Unit to Unit Voice Service Request CSBK\n\tprivate void uu_v_reg (boolean bits[])\t{\n\t\t// TODO : Full decoding of UU_V_Req\n\t\tdisplay[0]=\"Unit to Unit Voice Service Request\";\n\t}\n\t\n\t// Unit to Unit Service Answer Response CSBK\n\tprivate void uu_ans_rep (boolean bits[])\t{\n\t\t// TODO : Full decoding of UU_Ans_Rsp\n\t\tdisplay[0]=\"Unit to Unit Service Answer Response\";\n\t}\n\t\n\t// Negative Acknowledge Response CSBK\n\tprivate void nack_rsp (boolean bits[])\t{\n\t\t// TODO : Full decoding of NACK_Rsp\n\t\tdisplay[0]=\"Negative Acknowledge Response\";\n\t}\n\t\n\t// Capacity Plus\n    // A great deal of information on this type of packet was kindly provided by Eric Cottrell on the Radioreference forums\t\n\t// see http://forums.radioreference.com/digital-voice-decoding-software/209318-understanding-capacity-plus-trunking-6.html#post2078924\t\n\tprivate void big_m_csbko62 (DMRDecode theApp,boolean bits[])\t{\n\t\tint group1,group2,group3,group4,group5,group6,a,lcn;\n\t\tStringBuilder sb1=new StringBuilder(300);\n\t\tStringBuilder sb2=new StringBuilder(300);\n\t\tdisplay[0]=\"Capacity Plus CSBK : CSBKO=62\";\n\t\t// LCN\n\t\tif (bits[20]==true) lcn=8;\n\t\telse lcn=0;\n\t\tif (bits[21]==true) lcn=lcn+4;\n\t\tif (bits[22]==true) lcn=lcn+2;\n\t\tif (bits[23]==true) lcn++;\n\t\t// Group idents\n\t\t// Low group\n\t\tif (bits[32]==true) group1=128;\n\t\telse group1=0;\n\t\tif (bits[33]==true) group1=group1+64;\n\t\tif (bits[34]==true) group1=group1+32;\n\t\tif (bits[35]==true) group1=group1+16;\n\t\tif (bits[36]==true) group1=group1+8;\n\t\tif (bits[37]==true) group1=group1+4;\n\t\tif (bits[38]==true) group1=group1+2;\n\t\tif (bits[39]==true) group1++;\n\t\t// Group 2\n\t\tif (bits[40]==true) group2=128;\n\t\telse group2=0;\n\t\tif (bits[41]==true) group2=group2+64;\n\t\tif (bits[42]==true) group2=group2+32;\n\t\tif (bits[43]==true) group2=group2+16;\n\t\tif (bits[44]==true) group2=group2+8;\n\t\tif (bits[45]==true) group2=group2+4;\n\t\tif (bits[46]==true) group2=group2+2;\n\t\tif (bits[47]==true) group2++;\n\t\t// Group 3\n\t\tif (bits[48]==true) group3=128;\n\t\telse group3=0;\n\t\tif (bits[49]==true) group3=group3+64;\n\t\tif (bits[50]==true) group3=group3+32;\n\t\tif (bits[51]==true) group3=group3+16;\n\t\tif (bits[52]==true) group3=group3+8;\n\t\tif (bits[53]==true) group3=group3+4;\n\t\tif (bits[54]==true) group3=group3+2;\n\t\tif (bits[55]==true) group3++;\n\t\t// Group 4\n\t\tif (bits[56]==true) group4=128;\n\t\telse group4=0;\n\t\tif (bits[57]==true) group4=group4+64;\n\t\tif (bits[58]==true) group4=group4+32;\n\t\tif (bits[59]==true) group4=group4+16;\n\t\tif (bits[60]==true) group4=group4+8;\n\t\tif (bits[61]==true) group4=group4+4;\n\t\tif (bits[62]==true) group4=group4+2;\n\t\tif (bits[63]==true) group4++;\n\t\t// Group 5\n\t\tif (bits[64]==true) group5=128;\n\t\telse group5=0;\n\t\tif (bits[65]==true) group5=group5+64;\n\t\tif (bits[66]==true) group5=group5+32;\n\t\tif (bits[67]==true) group5=group5+16;\n\t\tif (bits[68]==true) group5=group5+8;\n\t\tif (bits[69]==true) group5=group5+4;\n\t\tif (bits[70]==true) group5=group5+2;\n\t\tif (bits[71]==true) group5++;\t\t\n\t\t// Group 6\n\t\tif (bits[72]==true) group6=128;\n\t\telse group6=0;\n\t\tif (bits[73]==true) group6=group6+64;\n\t\tif (bits[74]==true) group6=group6+32;\n\t\tif (bits[75]==true) group6=group6+16;\n\t\tif (bits[76]==true) group6=group6+8;\n\t\tif (bits[77]==true) group6=group6+4;\n\t\tif (bits[78]==true) group6=group6+2;\n\t\tif (bits[79]==true) group6++;\n\t\t// Display all this \n\t\t// Only show more if we have any activity\n\t\tif ((bits[24]==false)&&(bits[25]==false)&&(bits[26]==false)&&(bits[27]==false)&&(bits[28]==false)&&(bits[29]==false))\t{\n\t\t\tsb1.append(\"Activity Update : LCN \"+Integer.toString(lcn)+\" is the Rest Channel\");\n\t\t} else {\n\t\t\tboolean nf=false;\n\t\t\tsb1.append(\"Activity Update : LCN \"+Integer.toString(lcn)+\" is the rest channel (\");\n\t\t\tif (bits[24]==true)\t{\n\t\t\t\tif (group1>0) sb1.append(\"Group \"+Integer.toString(group1)+\" on LCN 1\");\n\t\t\t\telse sb1.append(\"Activity on LCN 1\");\n\t\t\t\tnf=true;\n\t\t\t}\n\t\t\tif (bits[25]==true)\t{\n\t\t\t\tif (nf==true) sb1.append(\",\");\n\t\t\t\tif (group2>0) sb1.append(\"Group \"+Integer.toString(group2)+\" on LCN 2\");\n\t\t\t\telse sb1.append(\"Activity on LCN 2\");\n\t\t\t\tnf=true;\n\t\t\t}\n\t\t\tif (bits[26]==true)\t{\n\t\t\t\tif (nf==true) sb1.append(\",\");\n\t\t\t\tif (group3>0) sb1.append(\"Group \"+Integer.toString(group3)+\" on LCN 3\");\n\t\t\t\telse sb1.append(\"Activity on LCN 3\");\n\t\t\t\tnf=true;\n\t\t\t}\n\t\t\tif (bits[27]==true)\t{\n\t\t\t\tif (nf==true) sb1.append(\",\");\n\t\t\t\tif (group4>0) sb1.append(\"Group \"+Integer.toString(group4)+\" on LCN 4\");\n\t\t\t\telse sb1.append(\"Activity on LCN 4\");\n\t\t\t\tnf=true;\n\t\t\t}\n\t\t\tif (bits[28]==true)\t{\n\t\t\t\tif (nf==true) sb1.append(\",\");\n\t\t\t\tif (group5>0) sb1.append(\"Group \"+Integer.toString(group5)+\" on LCN 5\");\n\t\t\t\telse sb1.append(\"Activity on LCN 5\");\n\t\t\t\tnf=true;\n\t\t\t}\n\t\t\tif (bits[29]==true)\t{\n\t\t\t\tif (nf==true) sb1.append(\",\");\n\t\t\t\tif (group6>0) sb1.append(\"Group \"+Integer.toString(group6)+\" on LCN 6\");\n\t\t\t\telse sb1.append(\"Activity on LCN 6\");\n\t\t\t\tnf=true;\n\t\t\t}\n\t\t\tif (nf==true) sb1.append(\")\");\n\t\t}\n\t\tdisplay[1]=sb1.toString();\n\t\t// Display the full binary if in debug mode\n\t\tif (theApp.isDebug()==true)\t{\n\t\t\tfor (a=16;a<80;a++)\t{\n\t\t\t\tif (bits[a]==true) sb2.append(\"1\");\n\t\t\t\telse sb2.append(\"0\");\n\t\t\t}\n\t\t\tdisplay[2]=sb2.toString();\n\t\t}\n\t}\n\t\n\t// Connect Plus - CSBKO 03 FID=6\n\tprivate void big_m_csbko03 (DMRDecode theApp,boolean bits[])\t{\n\t\tint a,lcn;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(300);\n\t\tStringBuilder sb2=new StringBuilder(300);\n\t\tdisplay[0]=\"Connect Plus CSBK : CSBKO=3\";\n\t\t// Source ID\n\t\tint source=utils.retAddress(bits,16);\n\t\t// Group address\n\t\tint group=utils.retAddress(bits,40);\n\t\t// LCN\n\t\tif (bits[64]==true) lcn=8;\n\t\telse lcn=0;\n\t\tif (bits[65]==true) lcn=lcn+4;\n\t\tif (bits[66]==true) lcn=lcn+2;\n\t\tif (bits[67]==true) lcn++;\n\t\t// Time Slot\n\t    // The information on the time slot bit was kindly provided by W8EMX on the Radioreference forums\n\t\t// see http://forums.radioreference.com/digital-voice-decoding-software/213131-understanding-connect-plus-trunking-7.html#post1909226\n\t\tboolean timeSlot=bits[68];\n\t\t// Display this\n\t\tsb1.append(\"Channel Grant : LCN \"+Integer.toString(lcn));\n\t\tif (timeSlot==false) sb1.append(\" TS1\");\n\t\telse sb1.append(\" TS2\");\n\t\tsb1.append(\" Source \"+Integer.toString(source));\n\t\tsb1.append(\" Group \"+Integer.toString(group));\n\t\tdisplay[1]=sb1.toString();\n\t\t// Display the full binary if in debug mode\n\t\tif (theApp.isDebug()==true)\t{\n\t\t\tfor (a=16;a<80;a++)\t{\n\t\t\t\tif (bits[a]==true) sb2.append(\"1\");\n\t\t\t\telse sb2.append(\"0\");\n\t\t\t}\n\t\t\tdisplay[2]=sb2.toString();\n\t\t}\n\t}\n\t\n\t// Connect Plus - CSBKO 01 FID=6\n\tprivate void big_m_csbko01 (DMRDecode theApp,boolean bits[])\t{\n\t\tint a,nb1,nb2,nb3,nb4,nb5;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(300);\n\t\tStringBuilder sb2=new StringBuilder(300);\n\t\tdisplay[0]=\"Connect Plus CSBK : CSBKO=1\";\n\t\tsb1.append(\"Control Channel Neighbour List : \");\n\t\t// The information to decode these packets was kindly provided by inigo88 on the Radioreference forums\n\t\t// see http://forums.radioreference.com/digital-voice-decoding-software/213131-understanding-connect-plus-trunking-6.html#post1866950\n\t\t//                 67 890123 45 678901 23 456789 01 234567 89 012345 6789 0123 4567 8901 2345 6789\n\t\t// CSBKO=1 + FID=6 00 000001 00 000011 00 000100 00 000101 00 000110 0000 0000 0000 0000 0000 1110\n\t\t//                         1         3         4\t     5         6\t                      \n\t\t// bits 16,17 have an unknown purpose\n\t\t// bits 18,19,20,21,22,23 make up the first neighbour site ID\n\t\tnb1=utils.retSix(bits,18);\n\t\t// bits 24,25 have an unknown purpose\n\t\t// bits 26,27,28,29,30,31 make up the second neighbour site ID\n\t\tnb2=utils.retSix(bits,26);\n\t\t// bits 32,33 have an unknown purpose\n\t\t// bits 34,35,36,37,38,39 make up the third neighbour site ID\n\t\tnb3=utils.retSix(bits,34);\n\t\t// bits 40,41 have an unknown purpose\n\t\t// bits 42,43,44,45,46,47 make up the fourth neighbour site ID\n\t\tnb4=utils.retSix(bits,42);\n\t\t// bits 48,49 have an unknown purpose\n\t\t// bits 50,51,52,53,54,55 make up the fifth neighbour site ID\n\t\tnb5=utils.retSix(bits,50);\n\t\t// bits 56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79 have an unknown purpose\n\t\t// Display this info\n\t\tsb1.append(Integer.toString(nb1)+\",\"+Integer.toString(nb2)+\",\"+Integer.toString(nb3)+\",\"+Integer.toString(nb4)+\",\"+Integer.toString(nb5)+\" (\");\n\t\t// Also display as raw binary for now\n\t\tfor (a=16;a<80;a++)\t{\n\t\t\tif (bits[a]==true) sb1.append(\"1\");\n\t\t\telse sb1.append(\"0\");\n\t\t}\n\t\tsb1.append(\")\");\n\t\tdisplay[1]=sb1.toString();\n\t\t// Display the full binary if in debug mode\n\t\tif (theApp.isDebug()==true)\t{\n\t\t\tfor (a=16;a<80;a++)\t{\n\t\t\t\tif (bits[a]==true) sb2.append(\"1\");\n\t\t\t\telse sb2.append(\"0\");\n\t\t\t}\n\t\t\tdisplay[2]=sb2.toString();\n\t\t}\n\t}\n\t\n\t// CSBKO 25 FID 00 C_ALOHA\n\t// Bits 16,17,18,19,20,21 Reserved\n\t// 22 Infill\n\t// 23 Active connection\n\t// 24,25,26,27,28 Mask\n\t// 29,30 Service Function\n\t// 31,32,33,34 NRand_Wait\n\t// 35 Reg\n\t// 36,37,38,39 Backoff\n\t// 40 - 55 System ID\n\t// 56 - 79 MS Individual addr\n\tprivate void csbko25fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tStringBuilder sb1=new StringBuilder(300);\n\t\tStringBuilder sb2=new StringBuilder(300);\n\t\tUtilities utils=new Utilities();\n\t\tdisplay[0]=\"C_ALOHA : CSBKO=25 + FID=0\";\n\t\t// Infill\n\t\tif (bits[22]==true) sb1.append(\"Infill Radio Site : \");\n\t\telse sb1.append(\"Not an Infill Radio Site : \");\n\t\t// Active_Connection\n\t\tif (bits[23]==true) sb1.append(\"TS has Network Connection : \");\n\t\telse sb1.append(\"TS doesn't have a Network Connection : \");\n\t\t// Mask\n\t\tint mask=0;\n\t\tif (bits[24]==true) mask=16;\n\t\tif (bits[25]==true) mask=mask+8;\n\t\tif (bits[26]==true) mask=mask+4;\n\t\tif (bits[27]==true) mask=mask+2;\n\t\tif (bits[28]==true) mask++;\n\t\tsb1.append(\"Mask=\"+Integer.toString(mask)+\" : \");\n\t\t// Service Function\n\t\tint sf=0;\n\t\tif (bits[29]==true) sf=2;\n\t\tif (bits[30]==true) sf++;\n\t\tsb1.append(\"Service Function=\"+Integer.toString(sf)+\" : \");\n\t\t// Reg\n\t\tif (bits[35]==true) sb1.append(\"TSCC demands MS must register\");\n\t\tdisplay[1]=sb1.toString();\n\t\t// System Identity Code\n\t\tint sysID=utils.retSixteen(bits,40);\n\t\tsb2.append(\"System Identity Code=\"+Integer.toString(sysID)+\" : \");\n\t\t// MS Address\n\t\tint addr=utils.retAddress(bits,56);\n\t\tsb2.append(\"MS Individual Address=\"+Integer.toString(addr));\n\t\tdisplay[2]=sb2.toString();\n\t}\n\t\n\t// C_AHOY\n\t// Bits\n\t// 16,17,18,19,20,21,22 Service_Options_Mirror\n\t// 23 Service_Kind_Flag\n\t// 24 Ambient Listening Service\n\t// 25 IG\n\t// 26,27 Appended Blocks\n\t// 28,29,30,31 Service_Kind\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko28fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tStringBuilder sb1=new StringBuilder(300);\n\t\tStringBuilder sb2=new StringBuilder(300);\n\t\tUtilities utils=new Utilities();\n\t\t// Service_Options_Mirror\n\t\tint service_options_mirror=utils.retSeven(bits,16);\n\t\t// Service_Kind_Flag\n\t\tboolean service_kind_flag=bits[23];\n\t\t// Ambient Listening Service\n\t\tboolean ambient_listening_service=bits[24];\n\t\t// IG\n\t\tboolean ig=bits[25];\n\t\t// Appended Blocks\n\t\tint appended_blocks=0;\n\t\tif (bits[26]==true) appended_blocks=2;\n\t\tif (bits[27]==true) appended_blocks++;\n\t\t// Service_Kind\n\t\tString service_kind;\n\t\tint sk=utils.retFour(bits,28);\n\t\tif (sk==0) service_kind=\"Individual Voice Call Service\";\n\t\telse if (sk==1) service_kind=\"Talkgroup Voice Call Service\";\n\t\telse if (sk==2) service_kind=\"Individual Packet Data Call Service\";\n\t\telse if (sk==3) service_kind=\"Packet Data Call Service to a talkgroup\";\n\t\telse if (sk==4) service_kind=\"Individual Short Data Call Service\";\n\t\telse if (sk==5) service_kind=\"Talkgroup Short Data Call Service\";\n\t\telse if (sk==6) service_kind=\"Short Data Polling Service\";\n\t\telse if (sk==7) service_kind=\"Status Transport Service\";\n\t\telse if (sk==8) service_kind=\"Call Diversion Service\";\n\t\telse if (sk==9) service_kind=\"Call Answer Service\";\n\t\telse if (sk==13) service_kind=\"Supplementary Service\";\n\t\telse if (sk==14) service_kind=\"Registration/Authentication Service/MS Radio Check\";\n\t\telse if (sk==15) service_kind=\"Cancel Call Service\";\n\t\telse service_kind=\"Reserved\";\n\t\t// Target address\n\t\tint targetAddr=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint sourceAddr=utils.retAddress(bits,56);\n\t\t// Display\n\t\tdisplay[0]=\"C_AHOY : CSBKO=28 + FID=0 : \"+service_kind;\n\t\tif (ambient_listening_service==true) display[0]=display[0]+\" : ALS Requested : \";\n\t\tif (appended_blocks>0) display[0]=display[0]+Integer.toString(appended_blocks)+\" Blocks Appended : \";\n\t\tdisplay[0]=display[0]+\"SOM=\"+Integer.toString(service_options_mirror);\n\t\tif (service_kind_flag==true) display[0]=display[0]+\" : SKF=1\";\n\t\telse display[0]=display[0]+\" : SKF=0\";\n\t\tsb1.append(\"Target : \");\n\t\tif (targetAddr==0xFFFECC) sb1.append(\"STUNI\");\n\t\telse if (targetAddr==0xFFFECF) sb1.append(\"KILLII\");\n\t\telse if (targetAddr==0xFFFECD) sb1.append(\"AUTHI\");\n\t\telse sb1.append(Integer.toString(targetAddr));\n\t\tif (ig==true) sb1.append (\" (TG)\");\n\t\tdisplay[1]=sb1.toString();\n\t\tsb2.append(\"Source : \");\n\t\tif (sourceAddr==0xFFFECC) sb2.append(\"STUNI\");\n\t\telse if (sourceAddr==0xFFFECF) sb2.append(\"KILLII\");\n\t\telse if (sourceAddr==0xFFFECD) sb2.append(\"AUTHI\");\n\t\telse sb2.append(Integer.toString(sourceAddr));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Record this\n\t\t// Log these users\n\t\t// Target\n\t\tif (targetAddr<0xFFFEC0)\t{\n\t\t\ttheApp.usersLogged.addUser(targetAddr);\t\n\t\t\tindex=theApp.usersLogged.findUserIndex(targetAddr);\n\t\t\tif (index!=-1) { \n\t\t\t\tif (ig==true) theApp.usersLogged.setAsGroup(index);\n\t\t\t}\n\t\t}\n\t\t// Source\n\t\tif (sourceAddr<0xFFFEC0) theApp.usersLogged.addUser(sourceAddr);\n\t}\n\t\n\t\n\t// CSBKO 40 FID 00 C_BCAST\n\t// Bits 16,17,18,19,20 Announcement type\n\t// 21 - 34 Broadcast Parms 1\n\t// 35 Reg\n\t// 36,37,38,39 Backoff\n\t// 40 - 55 System ID\n\t// 56 - 79 Broadcast Parms 2\n\tprivate void csbko40fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tStringBuilder sb1=new StringBuilder(300);\n\t\tStringBuilder sb2=new StringBuilder(300);\n\t\tUtilities utils=new Utilities();\n\t\t// Announcement Type\n\t\tint at=0;\n\t\tString aType;\n\t\tif (bits[16]==true) at=16;\n\t\tif (bits[17]==true) at=at+8;\n\t\tif (bits[18]==true) at=at+4;\n\t\tif (bits[19]==true) at=at+2;\n\t\tif (bits[20]==true) at++;\n\t\tif (at==0)\t{\n\t\t\taType=\"Ann-WD_TSCC (Announce/Withdraw TSCC)\";\n\t\t\t// Parms 1\n\t\t\t// Bits 21,22,23,24 are reserved\n\t\t\tint col_ch1=utils.retFour(bits,25);\n\t\t\tint col_ch2=utils.retFour(bits,29);\n\t\t\tboolean aw_flag1=bits[33];\n\t\t\tboolean aw_flag2=bits[34];\n\t\t\t// Parms 2\n\t\t\tint bcast_ch1=utils.retTwelve(bits,56);\n\t\t\t// T_MS-LINE_TIMER\n\t\t\tint bcast_ch2=utils.retTwelve(bits,68);\n\t\t\t// Display\n\t\t\tif (aw_flag1==true)\tsb1.append(\"Withdraw BCAST_CH1 (Colour Code \"+Integer.toString(col_ch1)+\") from the hunt list : \");\n\t\t\telse sb1.append(\"Add BCAST_CH1 (Colour Code \"+Integer.toString(col_ch1)+\") from the hunt list : \");\n\t\t\tif (aw_flag2==true)\tsb1.append(\"Withdraw BCAST_CH2 (Colour Code \"+Integer.toString(col_ch2)+\") from the hunt list : \");\n\t\t\telse sb1.append(\"Add BCAST_CH2 (Colour Code \"+Integer.toString(col_ch2)+\") from the hunt list : \");\n\t\t\tsb2.append(\"BCAST_CH1=\"+Integer.toString(bcast_ch1));\n\t\t\tsb2.append(\" : BCAST_CH2=\"+Integer.toString(bcast_ch2));\n\t\t\tdisplay[1]=sb1.toString();\n\t\t\tdisplay[2]=sb2.toString();\n\t\t}\n\t\telse if (at==1)\t{\n\t\t\taType=\"CallTimer_Parms (Specify Call Timer Parameters)\";\n\t\t\t// Parms 1\n\t\t\t// T_EMERG_TIMER\n\t\t\tint t_emerg_timer=utils.retNine(bits,21);\n\t\t\t// T_PACKET_TIMER\n\t\t\tint t_packet_timer=utils.retFive(bits,30);\n\t\t\t// Parms 2\n\t\t\t// T_MS-MS_TIMER\n\t\t\tint t_ms_ms_timer=utils.retTwelve(bits,56);\n\t\t\t// T_MS-LINE_TIMER\n\t\t\tint t_ms_line_timer=utils.retTwelve(bits,68);\n\t\t\t// Display these\n\t\t\tif (t_emerg_timer==512)\t{\n\t\t\t\tsb1.append(\"Emergency Call Timer is Infinity : \");\n\t\t\t}\n\t\t\telse\t{\n\t\t\t\tsb1.append(\"T_EMERG_TIMER=\"+Integer.toString(t_emerg_timer)+\" : \");\n\t\t\t}\n\t\t\tif (t_packet_timer==31)\t{\n\t\t\t\tsb1.append(\"Packet Call Timer is Infinity : \");\n\t\t\t}\n\t\t\telse\t{\n\t\t\t\tsb1.append(\"T_PACKET_TIMER=\"+Integer.toString(t_packet_timer)+\" : \");\n\t\t\t}\n\t\t\tif (t_ms_ms_timer==4095)\t{\n\t\t\t\tsb1.append(\"MS to MS Call Timer is Infinity : \");\n\t\t\t}\n\t\t\telse\t{\n\t\t\t\tsb1.append(\"T_MS-MS_TIMER=\"+Integer.toString(t_ms_ms_timer)+\" : \");\n\t\t\t}\n\t\t\tif (t_ms_line_timer==4095)\t{\n\t\t\t\tsb1.append(\"Line Connected Call Timer is Infinity\");\n\t\t\t}\n\t\t\telse\t{\n\t\t\t\tsb1.append(\"T_MS-LINE_TIMER=\"+Integer.toString(t_ms_line_timer)+\" : \");\n\t\t\t}\t\t\t\n\t\t\tdisplay[1]=sb1.toString();\n\t\t}\n\t\telse if (at==2)\t{\n\t\t\taType=\"Vote_Now (Vote Now Advice)\";\n\t\t\t// Parms1 contains the most significant 14 bits of the TSCC system identity\n\t\t\t// Parms2\n\t\t\t// Bits 56,57 least significant 2 bits of TSCC system identity is 'Manufacturer Specific' VN_ACTION option selected\n\t\t\t// Bit 58 active connection\n\t\t\t// Bits 59,60,61,62,63,64 Reserved\n\t\t\t// Site_Strategy\n\t\t\tint site_strat=utils.retThree(bits,65);\n\t\t\tif (site_strat==0) sb1.append(\"Radio Site : \");\n\t\t\telse if (site_strat==1) sb1.append(\"Infill : \");\n\t\t\telse if (site_strat==2) sb1.append(\"Manufacturer specific strategy : \");\n\t\t\telse sb1.append(\"Reserved : \");\n\t\t\t// CH_VOTE\n\t\t\tint ch_vote=utils.retTwelve(bits,68);\n\t\t\tsb1.append(\"CH_VOTE is \"+Integer.toString(ch_vote));\n\t\t\tdisplay[1]=sb1.toString();\n\t\t}\n\t\telse if (at==3)\t{\n\t\t\taType=\"Local_Time (Broadcast Local Time)\";\n\t\t\t// Parms 1\n\t\t\t// B_DAY \n\t\t\tint b_day=utils.retFive(bits,21);\n\t\t\t// B_MONTH \n\t\t\tint b_month=utils.retFour(bits,25);\n\t\t\t// UTC_OFFSET\n\t\t\tint utc_offset=utils.retFive(bits,30);\n\t\t\t// Check there is a date\n\t\t\tif ((b_day>0)&&(b_month>0))\t{\n\t\t\t\tsb1.append(\"Date \"+Integer.toString(b_day)+\"/\"+Integer.toString(b_month)+\" \");\n\t\t\t\tif (utc_offset==31) sb1.append(\"UTC Offset is \"+Integer.toString(utc_offset)+\" hours \");\n\t\t\t}\n\t\t\t// Parms 2\n\t\t\t// B_HOURS\n\t\t\tint b_hours=utils.retFive(bits,56);\n\t\t\t// B_MINS\n\t\t\tint b_mins=utils.retSix(bits,61);\n\t\t\t// B_SECS\n\t\t\tint b_secs=utils.retSix(bits,67);\n\t\t\t// DAYOF_WEEK\n\t\t\tint dayof_week=utils.retThree(bits,73);\n\t\t\t// UTC_OFFSET_FRACTION\n\t\t\tint utc_offset_fraction=0;\n\t\t\tif (bits[74]==true) utc_offset_fraction=2;\n\t\t\tif (bits[75]==true) utc_offset_fraction++;\n\t\t\t// 76,77,78,79 Reserved\n\t\t\tif (dayof_week==1) sb1.append(\"Sunday \");\n\t\t\telse if (dayof_week==2) sb1.append(\"Monday \");\n\t\t\telse if (dayof_week==3) sb1.append(\"Tuesday \");\n\t\t\telse if (dayof_week==4) sb1.append(\"Wednesday \");\n\t\t\telse if (dayof_week==5) sb1.append(\"Thursday \");\n\t\t\telse if (dayof_week==6) sb1.append(\"Friday \");\n\t\t\telse if (dayof_week==7) sb1.append(\"Saturday \");\n\t\t\tif (b_hours<10) sb1.append(\"0\");\n\t\t\tsb1.append(Integer.toString(b_hours)+\":\");\n\t\t\tif (b_mins<10) sb1.append(\"0\");\n\t\t\tsb1.append(Integer.toString(b_mins)+\":\");\n\t\t\tif (b_secs<10) sb1.append(\"0\");\n\t\t\tsb1.append(Integer.toString(b_secs));\n\t\t\tif (utc_offset_fraction==1) sb1.append(\" (Add 15 mins)\");\n\t\t\telse if (utc_offset_fraction==1) sb1.append(\" (Add 30 mins)\");\n\t\t\telse if (utc_offset_fraction==2) sb1.append(\" (Add 45 mins)\");\n\t\t\tdisplay[1]=sb1.toString();\n\t\t}\n\t\telse if (at==4)\t{\n\t\t\taType=\"MassReg (Mass_Registration)\";\n\t\t\t// Parms 1\n\t\t\t// 21,22,23,24,25 Reserved\n\t\t\t// Reg_Window\n\t\t\tint reg_window=utils.retFour(bits,26);\n\t\t\t// Aloha Mask\n\t\t\tint aloha_mask=utils.retFive(bits,30);\n\t\t\t// Parms 2\n\t\t\tint ms_individual_address=utils.retAddress(bits,26);\n\t\t\t// Display this\n\t\t\tsb1.append(\"Reg_Window=\"+Integer.toString(reg_window)+\" : Aloha Mask=\"+Integer.toString(aloha_mask)+\" : MS Individual Address \"+Integer.toString(ms_individual_address));\n\t\t\tdisplay[1]=sb1.toString();\n\t\t}\n\t\telse if (at==5)\t{\n\t\t\taType=\"Chan_Freq (Announce a logical channel/frequency relationship)\";\n\t\t\t// Nothing describing this is in ETSI TS 102 361-4 V1.5.1 so just show binary parms 1 & 2 instead\n\t\t\tint a;\n\t\t\tfor (a=21;a<35;a++)\t{\n\t\t\t\tif (bits[a]==false) sb1.append(\"0\");\n\t\t\t\telse sb1.append(\"1\");\n\t\t\t}\n\t\t\tsb1.append(\" \");\n\t\t\tfor (a=56;a<80;a++)\t{\n\t\t\t\tif (bits[a]==false) sb1.append(\"0\");\n\t\t\t\telse sb1.append(\"1\");\n\t\t\t}\t\t\n\t\t\tdisplay[1]=sb1.toString();\n\t\t}\n\t\telse if (at==6)\t{\n\t\t\taType=\"Adjacent_Site (Adjacent Site Information)\";\n\t\t\t// Parms1 contains the most significant 14 bits of the TSCC system identity\n\t\t\t// Parms2\n\t\t\t// Bits 56,57 least significant 2 bits of TSCC system identity is 'Manufacturer Specific' VN_ACTION option selected\n\t\t\t// Bit 58 active connection\n\t\t\t// Bits 59,60,61,62,63,64 Reserved\n\t\t\t// Site_Strategy\n\t\t\tint site_strat=utils.retThree(bits,65);\n\t\t\tif (site_strat==0) sb1.append(\"Radio Site : \");\n\t\t\telse if (site_strat==1) sb1.append(\"Infill : \");\n\t\t\telse if (site_strat==2) sb1.append(\"Manufacturer specific strategy : \");\n\t\t\telse sb1.append(\"Reserved : \");\n\t\t\t// CH_ADJ\n\t\t\tint ch_adj=utils.retTwelve(bits,68);\n\t\t\tsb1.append(\"CH_ADJ \"+Integer.toString(ch_adj));\n\t\t\tdisplay[1]=sb1.toString();\n\t\t}\n\t\telse if ((at==30)||(at==31))\t{\n\t\t\taType=\"Manufacturer Specific (\"+Integer.toString(at)+\")\";\n\t\t\t// Display the parms binary\n\t\t\tint a;\n\t\t\tfor (a=21;a<35;a++)\t{\n\t\t\t\tif (bits[a]==false) sb1.append(\"0\");\n\t\t\t\telse sb1.append(\"1\");\n\t\t\t}\n\t\t\tsb1.append(\" \");\n\t\t\tfor (a=56;a<80;a++)\t{\n\t\t\t\tif (bits[a]==false) sb1.append(\"0\");\n\t\t\t\telse sb1.append(\"1\");\n\t\t\t}\t\t\n\t\t\tdisplay[1]=sb1.toString();\t\t\t\n\t\t}\n\t\telse aType=\"Reserved (\"+Integer.toString(at)+\")\";\n\t\t// System Identity Code\n\t\tint sysID=utils.retSixteen(bits,40);\n\t\tdisplay[0]=\"C_BCAST : CSBKO=40 + FID=0 : System ID=\"+Integer.toString(sysID)+\" : \"+aType;\n\t}\t\n\t\n\t// C_ACKVIT\n\t// Bits ..\n\t// 16,17,18,19,20,21,22 Service_Options_Mirror\n\t// 23 Service_Kind_Flag \n\t// 24,25 Reserved\n\t// 26,27 Appended_Blocks\n\t// 28,29,30,31 Service_Kind\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko30fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\t// Service Options Mirror\n\t\tint service_options_mirror=utils.retSeven(bits,16);\n\t\tdisplay[0]=\"C_ACKVIT : CSBKO=30 + FID=0 : Service_Options_Mirror=\"+Integer.toString(service_options_mirror);\n\t\t// Service_Kind_Flag\n\t\tboolean skf=bits[23];\n\t\t// Appended_Blocks\n\t\tint ablocks=0;\n\t\tif (bits[26]==true) ablocks=2;\n\t\tif (bits[27]==true) ablocks++;\n\t\t// Service_Kind\n\t\tint service_kind=utils.retFour(bits,28);\n\t\t// Display this\n\t\tif (skf==true) sb1.append(\"Service_Kind_Flag=1 : \");\n\t\telse sb1.append(\"Service_Kind_Flag=0 : \");\n\t\tsb1.append(\"Appended_Blocks=\"+Integer.toString(ablocks)+\" : \");\n\t\tsb1.append(\"Service_Kind=\"+Integer.toString(service_kind));\n\t\tdisplay[1]=sb1.toString();\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,56);\n\t\tsb2.append(\"Target Address : \"+Integer.toString(target));\n\t\tsb2.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Log these users\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\t\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t}\t\t\n\t\n\t// C_RAND\n\t// Bits ..\n\t// 16,17,18,19,20,21,22 Service_Options\n\t// 23 Proxy Flag\n\t// 24,25 Appended_Supplementary_Data\n\t// 26,27 Appended_Short_Data\n\t// 28,29,30,31 Service_Kind\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko31fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\t// Service Options\n\t\tint service_options=utils.retSeven(bits,16);\n\t\tdisplay[0]=\"C_RAND : CSBKO=31 + FID=0 : Service_Options=\"+Integer.toString(service_options);\n\t\t// Proxy Flag\n\t\tboolean pf=bits[23];\n\t\t// Appended_Supplementary_Data\n\t\tint asd=0;\n\t\tif (bits[24]==true) asd=2;\n\t\tif (bits[24]==true) asd++;\n\t\t// Appended_Short_Data\n\t\tint ashortd=0;\n\t\tif (bits[24]==true) ashortd=2;\n\t\tif (bits[25]==true) ashortd++;\n\t\t// Service_Kind\n\t\tint service_kind=utils.retFour(bits,28);\n\t\t// Display this\n\t\tif (pf==true) sb1.append(\"Proxy Flag=1 : \");\n\t\telse sb1.append(\"Proxy Flag=0 : \");\n\t\tsb1.append(\"Appended_Supplementary_Data=\"+Integer.toString(asd)+\" : \");\n\t\tsb1.append(\"Appended_Short_Data=\"+Integer.toString(ashortd)+\" : \");\n\t\tsb1.append(\"Service_Kind=\"+Integer.toString(service_kind));\n\t\tdisplay[1]=sb1.toString();\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,56);\n\t\tsb2.append(\"Target Address : \"+Integer.toString(target));\n\t\tsb2.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Log these users\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\t\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t}\t\n\n\t// CSBKO 31 FID 16 Call Alert\n\t// The information to decode this was kindly provided by bben95 on the Radioreference forums\n\t// http://forums.radioreference.com/digital-voice-decoding-software/191957-java-program-decode-dmr-31.html#post2098983\n\t// 0000000000000000 000000000001011101110010 000000000001011101110001\n\t// 1111222222222233 333333334444444444555555 555566666666667777777777\n\t// 6789012345678901 234567890123456789012345 678901234567890123456789\n\t// is Call alert from 6001 to 6002\n\tprivate void csbko31fid16 (DMRDecode theApp,boolean bits[])\t{\n\t\tint a;\n\t\tUtilities utils=new Utilities();\n\t\tint to=utils.retAddress(bits,32);\n\t\tint from=utils.retAddress(bits,56);\n\t\tStringBuilder sb1=new StringBuilder(300);\n\t\tdisplay[0]=\"CSBK : CSBKO=31 + FID=16\";\n\t\tsb1.append(\"Call Alert from \"+Integer.toString(from)+\" to \"+Integer.toString(to)+\" (\");\n\t\t// Also display the unknown part as raw binary for now\n\t\tfor (a=16;a<32;a++)\t{\n\t\t\tif (bits[a]==true) sb1.append(\"1\");\n\t\t\telse sb1.append(\"0\");\n\t\t\t}\n\t\tsb1.append(\")\");\n\t\tdisplay[1]=sb1.toString();\t\n\t}\n\t\n\t// C_ACKD\n\t// Bits ..\n\t// 16,17,18,19,20,21,22 Response_Info\n\t// 23,24,25,26,27,28,29,30 Reason Code\n\t// 31 Reserved\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko32fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\t// Response_Info\n\t\tint response_info=utils.retSeven(bits,16);\n\t\tdisplay[0]=\"C_ACKD : CSBKO=32 + FID=0 : Response_Info=\"+Integer.toString(response_info);\n\t\t// Reason Code\n\t\tint reason_code=utils.retEight(bits,23);\n\t\tint rc_t=(reason_code&192)>>6;\n\t\tif (rc_t==0) sb1.append(\"NACK : \");\n\t\telse if (rc_t==1) sb1.append(\"ACK : \");\n\t\telse if (rc_t==2) sb1.append(\"QACK : \");\n\t\telse if (rc_t==3) sb1.append(\"WACK : \");\n\t\tif ((reason_code&32)>0) sb1.append(\"TS to MS : \");\n\t\telse sb1.append(\"MS to TS : \");\n\t\tint ar=reason_code&31;\n\t\tsb1.append(getAckReason(rc_t,ar));\n\t\tdisplay[1]=sb1.toString();\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,56);\n\t\tsb2.append(\"Target Address : \"+Integer.toString(target));\n\t\tsb2.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Log these users\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\t\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t}\n\t\n\t// CSBKO 32 FID 16 Call Alert Ack\n\t// The information to decode this was kindly provided by bben95 on the Radioreference forums\n\t// http://forums.radioreference.com/digital-voice-decoding-software/191957-java-program-decode-dmr-31.html#post2098983\n    // 1001111100000000 000000000001011101110001 000000000001011101110010\n\t// 1111222222222233 333333334444444444555555 555566666666667777777777\n\t// 6789012345678901 234567890123456789012345 678901234567890123456789\n\t// is Call alert from 6001 to 6002: acknowledged\n\tprivate void csbko32fid16 (DMRDecode theApp,boolean bits[])\t{\n\t\tint a;\n\t\tUtilities utils=new Utilities();\n\t\tint to=utils.retAddress(bits,32);\n\t\tint from=utils.retAddress(bits,56);\n\t\tStringBuilder sb1=new StringBuilder(300);\n\t\tdisplay[0]=\"CSBK : CSBKO=32 + FID=16\";\n\t\tsb1.append(\"Call Alert ACK from \"+Integer.toString(from)+\" to \"+Integer.toString(to)+\" (\");\n\t\t// Also display the unknown part as raw binary for now\n\t\tfor (a=16;a<32;a++)\t{\n\t\t\tif (bits[a]==true) sb1.append(\"1\");\n\t\t\telse sb1.append(\"0\");\n\t\t\t}\n\t\tsb1.append(\")\");\n\t\tdisplay[1]=sb1.toString();\t\t\n\t}\n\t\n\t// C_ACKU\n\t// Bits ..\n\t// 16,17,18,19,20,21,22 Response_Info\n\t// 23,24,25,26,27,28,29,30 Reason Code\n\t// 31 Reserved\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko33fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\t// Response_Info\n\t\tint response_info=utils.retSeven(bits,16);\n\t\tdisplay[0]=\"C_ACKU : CSBKO=33 + FID=0 : Response_Info=\"+Integer.toString(response_info);\n\t\t// Reason Code\n\t\tint reason_code=utils.retEight(bits,23);\n\t\tint rc_t=(reason_code&192)>>6;\n\t\tif (rc_t==0) sb1.append(\"NACK : \");\n\t\telse if (rc_t==1) sb1.append(\"ACK : \");\n\t\telse if (rc_t==2) sb1.append(\"QACK : \");\n\t\telse if (rc_t==3) sb1.append(\"WACK : \");\n\t\tif ((reason_code&32)>0) sb1.append(\"TS to MS : \");\n\t\telse sb1.append(\"MS to TS : \");\n\t\tint ar=reason_code&31;\n\t\tsb1.append(getAckReason(rc_t,ar));\n\t\tdisplay[1]=sb1.toString();\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,56);\n\t\tsb2.append(\"Target Address : \"+Integer.toString(target));\n\t\tsb2.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Log these users\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\t\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t}\t\n\t\n\t\n\t// CSBKO 36 FID 16 Radio Check\n\tprivate void csbko36fid16 (DMRDecode theApp,boolean bits[])\t{\n\t\tint a;\n\t\tUtilities utils=new Utilities();\n\t\tint from=utils.retAddress(bits,32);\n\t\tint to=utils.retAddress(bits,56);\n\t\tStringBuilder sb1=new StringBuilder(300);\n\t\tdisplay[0]=\"CSBK : CSBKO=36 + FID=16\";\n\t\tsb1.append(\"Radio Check from \"+Integer.toString(from)+\" to \"+Integer.toString(to)+\" (\");\n\t\t// Also display the unknown part as raw binary for now\n\t\tfor (a=16;a<32;a++)\t{\n\t\t\tif (bits[a]==true) sb1.append(\"1\");\n\t\t\telse sb1.append(\"0\");\n\t\t\t}\n\t\tsb1.append(\")\");\n\t\tdisplay[1]=sb1.toString();\t\t\n\t}\t\n\t\n\t// P_MAINT\n\t// Bits ..\n\t// 16,17,18,19,20,21,22,23,24,25,26,27 Reserved\n\t// 28,29,30 Maint Kind\n\t// 31 Reserved\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko42fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\t// Maint Kind\n\t\tString mks;\n\t\tint maint_kind=utils.retThree(bits,28);\n\t\tif (maint_kind==0) mks=\"Disconnect. End of payload channel use\";\n\t\telse mks=\"Reserved\";\n\t\tdisplay[0]=\"P_MAINT : CSBKO=42 + FID=0 : \"+mks;\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,56);\n\t\tsb1.append(\"Target Address : \"+Integer.toString(target));\n\t\tsb1.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[1]=sb1.toString();\n\t\t// Log these users\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\t\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t}\t\t\t\n\t\n\t// Capacity Plus\n    // The information on this type of packet was kindly provided by Eric Cottrell on the Radioreference forums\t\n\t// see http://forums.radioreference.com/digital-voice-decoding-software/209318-understanding-capacity-plus-trunking-6.html#post2106396\n\tprivate void big_m_csbko59 (DMRDecode theApp,boolean bits[])\t{\n\t\tStringBuilder sb1=new StringBuilder(300);\n\t\tStringBuilder sb2=new StringBuilder(300);\n\t\tdisplay[0]=\"Capacity Plus CSBK : CSBKO=59 + FID=16: SYSSITESTS\";\n\t\t// Bits 16 & 17 First/Last Block\n\t\tint fl=0;\n\t\tif (bits[16]==true) fl=2;\n\t\tif (bits[17]==true) fl++;\n\t\tsb1.append(\"FL=\"+Integer.toString(fl));\n\t\t// Bit 18 slot\n\t\tif (bits[18]==false) sb1.append(\" : TS1\");\n\t\telse sb1.append(\" : TS2\");\n\t\t// Bits 19,20,21,22,23  Rest Channel ID\n\t\tint restCh=0;\n\t\tif (bits[19]==true) restCh=16;\n\t\telse if (bits[20]==true) restCh=restCh+8;\n\t\telse if (bits[21]==true) restCh=restCh+4;\n\t\telse if (bits[22]==true) restCh=restCh+2;\n\t\telse if (bits[23]==true) restCh++;\n\t\tsb1.append(\" : Rest Channel ID \"+Integer.toString(restCh));\n\t\t// Bit 24 ASYNC\n\t\tif (bits[24]==false) sb1.append(\" : Periodic Beacons\");\n\t\telse sb1.append(\" : Asynchronous Beacons\");\n\t\t// Bits 25,26,27,28 My Site ID\n\t\tint mySiteID=0;\n\t\tif (bits[25]==true) mySiteID=8;\n\t\tif (bits[26]==true) mySiteID=mySiteID+4;\n\t\tif (bits[27]==true) mySiteID=mySiteID+2;\n\t\tif (bits[28]==true) mySiteID++;\n\t\tsb1.append(\" : This Site ID \"+Integer.toString(mySiteID));\n\t\t// Display this\n\t\tdisplay[1]=sb1.toString();\n\t\t// Bits 29.30,31 Number of neighbour sites\n\t\tint nNos=0;\n\t\tif (bits[29]==true) nNos=4;\n\t\tif (bits[30]==true) nNos=nNos+2;\n\t\tif (bits[31]==true) nNos++;\n\t\t// If more than 6 sites we have a problem that will cause an overflow\n\t\tif (nNos>6) nNos=6;\n\t\t// Display the neighbour site info\n\t\tUtilities utils=new Utilities();\n\t\tint a,pos=32,nsid,nrst;\n\t\tfor (a=0;a<nNos;a++)\t{\n\t\t\tnsid=utils.retFour(bits,pos);\n\t\t\tnrst=utils.retFour(bits,pos+4);\n\t\t\t// Display\n\t\t\tif (a>0) sb2.append(\",\");\n\t\t\tsb2.append(\"Site #\"+Integer.toString(a+1)+\" ID \"+Integer.toString(nsid)+\" Rest Ch \"+Integer.toString(nrst));\n\t\t\t// Move along\n\t\t\tpos=pos+8;\n\t\t}\n\t\t// If there is any neighbour site info then display it\n\t\tif (nNos>0) display[2]=sb2.toString();\n\t}\n\t\n\t// P_CLEAR\n\t// Bits ..\n\t// 16,17,18,19,20,21,22,23,24,25,26,27 Logical Physical Channel Number\n\t// 28,29,30 Reserved\n\t// 31 IG\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko46fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\t// Logical channel number\n\t\tint lochan=utils.retTwelve(bits,16);\n\t\tdisplay[0]=\"P_Clear : CSBKO=46 + FID=0 from channel \"+Integer.toString(lochan);\n\t\t// IG\n\t\tboolean ig=bits[31];\n\t\t// Target address\n\t\tint targetAddr=utils.retAddress(bits,32);\n\t\t// Display this\n\t\t// Is this an ALLMSI\n\t\tif (targetAddr==0xFFFED4)\t{\n\t\t\tsb1.append(\"Target : ALLMSI\");\n\t\t}\n\t\telse\t{\n\t\t\tif (ig==true) sb1.append(\"Target TG :\");\n\t\t\telse sb1.append(\"Target : \");\n\t\t\tsb1.append(Integer.toString(targetAddr));\n\t\t}\n\t\tdisplay[1]=sb1.toString();\n\t\t// Source Address\n\t\tint sourceAddr=utils.retAddress(bits,56);\n\t\tsb2.append(\"Source Address \"+Integer.toString(sourceAddr));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Record this\n\t\t// Log these users\n\t\t// Target\n\t\tif (targetAddr!=0xFFFED4)\t{\n\t\t\ttheApp.usersLogged.addUser(targetAddr);\t\n\t\t\tindex=theApp.usersLogged.findUserIndex(targetAddr);\n\t\t\tif (index!=-1)\t{\n\t\t\t\tif (ig==true) theApp.usersLogged.setAsGroup(index);\n\t\t\t\ttheApp.usersLogged.setChannel(index,lochan);\n\t\t\t}\n\t\t}\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(sourceAddr);\n\t\tindex=theApp.usersLogged.findUserIndex(sourceAddr);\n\t\t// Quick log\n\t\tif (theApp.isQuickLog()==true) theApp.quickLogData(\"P_CLEAR\",targetAddr,sourceAddr,lochan,\"\");\n\t}\n\t\n\t// P_PROTECT\n\t// bits\n\t// 16,17,18,19,20,21,22,23,24,25,26,27 Reserved\n\t// 28,29,30 Protect_Kind\n\t// 31 IG\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko47fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\t// Protect_Kind\n\t\tString protect_kind;\n\t\tint pk=utils.retThree(bits,28);\n\t\tif (pk==0) protect_kind=\"DIS_PTT (Disable Target MS or Talkgroup transmission)\";\n\t\telse if (pk==1) protect_kind=\"EN_PTT (Enable Target MS or Talkgroup transmission)\";\n\t\telse if (pk==2) protect_kind=\"ILLEGALLY_PARKED (Clear down from the payload channel, MS whose address does not match Source or Target Address)\";\n\t\telse protect_kind=\"Reserved\";\n\t\tdisplay[0]=\"P_PROTECT : CSBKO=47 + FID=0 : \"+protect_kind;\n\t\t// IG\n\t\tboolean ig=bits[31];\n\t\t// Target address\n\t\tint targetAddr=utils.retAddress(bits,32);\n\t\t// Display this\n\t\t// Is this an ALLMSI\n\t\tif (targetAddr==0xFFFED4)\t{\n\t\t\tsb1.append(\"Target : ALLMSI\");\n\t\t}\n\t\telse\t{\n\t\t\tif (ig==true) sb1.append(\"Target TG :\");\n\t\t\telse sb1.append(\"Target : \");\n\t\t\tsb1.append(Integer.toString(targetAddr));\n\t\t}\n\t\tdisplay[1]=sb1.toString();\n\t\t// Source Address\n\t\tint sourceAddr=utils.retAddress(bits,56);\n\t\tsb2.append(\"Source Address \"+Integer.toString(sourceAddr));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Record this\n\t\t// Log these users\n\t\t// Target\n\t\tif (targetAddr!=0xFFFED4)\t{\n\t\t\ttheApp.usersLogged.addUser(targetAddr);\t\n\t\t\tindex=theApp.usersLogged.findUserIndex(targetAddr);\n\t\t\tif (index!=-1)\t{\n\t\t\t\tif (ig==true) theApp.usersLogged.setAsGroup(index);\n\t\t\t}\n\t\t}\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(sourceAddr);\n\t\tindex=theApp.usersLogged.findUserIndex(sourceAddr);\n\t\t// Quick log\n\t\tif (theApp.isQuickLog()==true) theApp.quickLogData(\"P_PROTECT\",targetAddr,sourceAddr,0,protect_kind);\t\n\t}\n\t\n\t\n\t// PV_GRANT\n\t// Bits ..\n\t// 16,17,18,19,20,21,22,23,24,25,26,27 Logical Physical Channel Number\n\t// 28 TDMA Channel\n\t// 29 OVCM\n\t// 30 Emergency\n\t// 31 Offset\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko48fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\tdisplay[0]=\"Private Voice Channel Grant : CSBKO=48 + FID=0\";\n\t\t// Logical Physical Channel Number\n\t\tint lchannel=utils.retTwelve(bits,16);\n\t\tsb1.append(\"Payload Channel \"+Integer.toString(lchannel));\n\t\tif (bits[28]==false) sb1.append(\" TDMA ch1 \");\n\t\telse sb1.append(\" TDMA ch2 \");\n\t\tif (bits[29]==true) sb1.append(\": OVCM Call \");\n\t\tif (bits[30]==true) sb1.append(\": Emergency Call \");\n\t\tif (bits[31]==false) sb1.append(\": Aligned Timing\");\n\t\telse sb1.append(\": Offset Timing\");\n\t\tdisplay[1]=sb1.toString();\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,56);\n\t\tsb2.append(\"Target Address : \"+Integer.toString(target));\n\t\tsb2.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Log these users\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\t\n\t\tindex=theApp.usersLogged.findUserIndex(target);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsUnitUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,lchannel);\n\t\t}\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t\tindex=theApp.usersLogged.findUserIndex(source);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsUnitUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,lchannel);\n\t\t}\n\t\t// Display this in a label on the status bar\n\t\tStringBuilder lab=new StringBuilder(250);\n\t\tlab.append(\"PV_GRANT from \");\n\t\tlab.append(Integer.toString(source));\n\t\tlab.append(\" to \");\n\t\tlab.append(Integer.toString(target));\n\t\tif (theApp.currentChannel==1) theApp.setCh1Label(lab.toString(),theApp.labelBusyColour);\n\t\telse theApp.setCh2Label(lab.toString(),theApp.labelBusyColour);\n\t\t// Quick log\n\t\tif (theApp.isQuickLog()==true) theApp.quickLogData(\"Private Voice Channel Grant\",target,source,lchannel,display[1]);\n\t}\n\t\n\t// TV_GRANT\n\t// Bits ..\n\t// 16,17,18,19,20,21,22,23,24,25,26,27 Logical Physical Channel Number\n\t// 28 TDMA Channel\n\t// 29 OVCM\n\t// 30 Emergency\n\t// 31 Offset\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko49fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\tdisplay[0]=\"Talkgroup Voice Channel Grant : CSBKO=49 + FID=0\";\n\t\t// Logical Physical Channel Number\n\t\tint lchannel=utils.retTwelve(bits,16);\n\t\tsb1.append(\"Payload Channel \"+Integer.toString(lchannel));\n\t\tif (bits[28]==false) sb1.append(\" TDMA ch1 \");\n\t\telse sb1.append(\" TDMA ch2 \");\n\t\tif (bits[29]==true) sb1.append(\": OVCM Call \");\n\t\tif (bits[30]==true) sb1.append(\": Emergency Call \");\n\t\tif (bits[31]==false) sb1.append(\": Aligned Timing\");\n\t\telse sb1.append(\": Offset Timing\");\n\t\tdisplay[1]=sb1.toString();\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,56);\n\t\tsb2.append(\"Target Address : \"+Integer.toString(target));\n\t\tsb2.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Log these users\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\t\n\t\tindex=theApp.usersLogged.findUserIndex(target);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsGroup(index);\n\t\t\ttheApp.usersLogged.setChannel(index,lchannel);\n\t\t}\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t\tindex=theApp.usersLogged.findUserIndex(source);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsGroupUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,lchannel);\n\t\t}\n\t\t// Display this in a label on the status bar\n\t\tStringBuilder lab=new StringBuilder(250);\n\t\tlab.append(\"TV_GRANT from \");\n\t\tlab.append(Integer.toString(source));\n\t\tlab.append(\" to \");\n\t\tlab.append(Integer.toString(target));\n\t\tif (theApp.currentChannel==1) theApp.setCh1Label(lab.toString(),theApp.labelBusyColour);\n\t\telse theApp.setCh2Label(lab.toString(),theApp.labelBusyColour);\n\t\t// Quick log\n\t\tif (theApp.isQuickLog()==true) theApp.quickLogData(\"Talkgroup Voice Channel Grant\",target,source,lchannel,display[1]);\n\t}\t\n\t\n\t// BTV_GRANT\n\t// Bits ..\n\t// 16,17,18,19,20,21,22,23,24,25,26,27 Logical Physical Channel Number\n\t// 28 TDMA Channel\n\t// 29 OVCM\n\t// 30 Emergency\n\t// 31 Offset\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko50fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\tdisplay[0]=\"Broadcast Talkgroup Voice Channel Grant : CSBKO=50 + FID=0\";\n\t\t// Logical Physical Channel Number\n\t\tint lchannel=utils.retTwelve(bits,16);\n\t\tsb1.append(\"Payload Channel \"+Integer.toString(lchannel));\n\t\tif (bits[28]==false) sb1.append(\" TDMA ch1 \");\n\t\telse sb1.append(\" TDMA ch2 \");\n\t\tif (bits[29]==true) sb1.append(\": OVCM Call \");\n\t\tif (bits[30]==true) sb1.append(\": Emergency Call \");\n\t\tif (bits[31]==false) sb1.append(\": Aligned Timing\");\n\t\telse sb1.append(\": Offset Timing\");\n\t\tdisplay[1]=sb1.toString();\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,56);\n\t\tsb2.append(\"Target Address : \"+Integer.toString(target));\n\t\tsb2.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Log these users\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\t\n\t\tindex=theApp.usersLogged.findUserIndex(target);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsGroup(index);\n\t\t\ttheApp.usersLogged.setChannel(index,lchannel);\n\t\t}\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t\tindex=theApp.usersLogged.findUserIndex(source);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsGroupUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,lchannel);\n\t\t}\n\t\t// Display this in a label on the status bar\n\t\tStringBuilder lab=new StringBuilder(250);\n\t\tlab.append(\"BTV_GRANT from \");\n\t\tlab.append(Integer.toString(source));\n\t\tlab.append(\" to \");\n\t\tlab.append(Integer.toString(target));\n\t\tif (theApp.currentChannel==1) theApp.setCh1Label(lab.toString(),theApp.labelBusyColour);\n\t\telse theApp.setCh2Label(lab.toString(),theApp.labelBusyColour);\n\t\t// Quick log\n\t\tif (theApp.isQuickLog()==true) theApp.quickLogData(\"Broadcast Talkgroup Voice Channel Grant\",target,source,lchannel,display[1]);\n\t}\t\t\n\t\n\t// PD_GRANT\n\t// Bits ..\n\t// 16,17,18,19,20,21,22,23,24,25,26,27 Logical Physical Channel Number\n\t// 28 TDMA Channel\n\t// 29 Packet Mode\n\t// 30 Emergency\n\t// 31 Offset\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko51fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\tdisplay[0]=\"Private Data Channel Grant : CSBKO=51 + FID=0\";\n\t\t// Logical Physical Channel Number\n\t\tint lchannel=utils.retTwelve(bits,16);\n\t\tsb1.append(\"Payload Channel \"+Integer.toString(lchannel));\n\t\tif (bits[28]==false) sb1.append(\" TDMA ch1 \");\n\t\telse sb1.append(\" TDMA ch2 \");\n\t\tif (bits[29]==true) sb1.append(\": Payload Channel uses 1:1 mode \");\n\t\telse sb1.append(\": Payload Channel uses 2:1 mode \");\n\t\tif (bits[30]==true) sb1.append(\": Emergency Call \");\n\t\tif (bits[31]==false) sb1.append(\": Aligned Timing\");\n\t\telse sb1.append(\": Offset Timing\");\n\t\tdisplay[1]=sb1.toString();\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,56);\n\t\tsb2.append(\"Target Address : \"+Integer.toString(target));\n\t\tsb2.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Log these users\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\t\n\t\tindex=theApp.usersLogged.findUserIndex(target);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsUnitUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,lchannel);\n\t\t}\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t\tindex=theApp.usersLogged.findUserIndex(source);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsUnitUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,lchannel);\n\t\t}\n\t\t// Display this in a label on the status bar\n\t\tStringBuilder lab=new StringBuilder(250);\n\t\tlab.append(\"PD_GRANT from \");\n\t\tlab.append(Integer.toString(source));\n\t\tlab.append(\" to \");\n\t\tlab.append(Integer.toString(target));\n\t\tif (theApp.currentChannel==1) theApp.setCh1Label(lab.toString(),theApp.labelBusyColour);\n\t\telse theApp.setCh2Label(lab.toString(),theApp.labelBusyColour);\n\t\t// Quick log\n\t\tif (theApp.isQuickLog()==true) theApp.quickLogData(\"Private Data Channel Grant\",target,source,lchannel,display[1]);\n\t}\n\t\n\t// TD_GRANT\n\t// Bits ..\n\t// 16,17,18,19,20,21,22,23,24,25,26,27 Logical Physical Channel Number\n\t// 28 TDMA Channel\n\t// 29 Packet Mode\n\t// 30 Emergency\n\t// 31 Offset\n\t// 32 - 55 Target Address\n\t// 56 - 79 Source Address\n\tprivate void csbko52fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\tdisplay[0]=\"Talkgroup Data Channel Grant : CSBKO=52 + FID=0\";\n\t\t// Logical Physical Channel Number\n\t\tint lchannel=utils.retTwelve(bits,16);\n\t\tsb1.append(\"Payload Channel \"+Integer.toString(lchannel));\n\t\tif (bits[28]==false) sb1.append(\" TDMA ch1 \");\n\t\telse sb1.append(\" TDMA ch2 \");\n\t\tif (bits[29]==true) sb1.append(\": Payload Channel uses 1:1 mode \");\n\t\telse sb1.append(\": Payload Channel uses 2:1 mode \");\n\t\tif (bits[30]==true) sb1.append(\": Emergency Call \");\n\t\tif (bits[31]==false) sb1.append(\": Aligned Timing\");\n\t\telse sb1.append(\": Offset Timing\");\n\t\tdisplay[1]=sb1.toString();\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,32);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,56);\n\t\tsb2.append(\"Target Address : \"+Integer.toString(target));\n\t\tsb2.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[2]=sb2.toString();\n\t\t// Log these users\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\t\n\t\tindex=theApp.usersLogged.findUserIndex(target);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsGroup(index);\n\t\t\ttheApp.usersLogged.setChannel(index,lchannel);\n\t\t}\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t\tindex=theApp.usersLogged.findUserIndex(source);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsGroupUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,lchannel);\n\t\t}\n\t\t// Display this in a label on the status bar\n\t\tStringBuilder lab=new StringBuilder(250);\n\t\tlab.append(\"TD_GRANT from \");\n\t\tlab.append(Integer.toString(source));\n\t\tlab.append(\" to \");\n\t\tlab.append(Integer.toString(target));\n\t\tif (theApp.currentChannel==1) theApp.setCh1Label(lab.toString(),theApp.labelBusyColour);\n\t\telse theApp.setCh2Label(lab.toString(),theApp.labelBusyColour);\n\t\t// Quick log\n\t\tif (theApp.isQuickLog()==true) theApp.quickLogData(\"Talkgroup Data Channel Grant\",target,source,lchannel,display[1]);\n\t}\t\n\t\n\t// C_MOVE\n\t// Bits ..\n\t// 16,17,18,19,20,21,22,23,24 Reserved\n\t// 25,26,27,28,29 Mask\n\t// 30,31,32,33,34 Reserved\n\t// 35 Reg\n\t// 36,37,38,39 Backoff\n\t// 40,41,42,43 Reserved\n\t// 44 - 55 Physical Channel Number\n\t// 56 - 79 MS Individual Address\n\tprivate void csbko57fid0 (DMRDecode theApp,boolean bits[])\t{\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb1=new StringBuilder(250);\n\t\tStringBuilder sb2=new StringBuilder(250);\n\t\tdisplay[0]=\"C_MOVE : CSBKO=57 + FID=0\";\n\t\t// Mask\n\t\tint mask=utils.retFive(bits,25);\n\t\tsb1.append(\"Mask=\"+Integer.toString(mask)+\" : \");\n\t\t// Reg\n\t\tif (bits[35]==true) sb1.append(\"TSCC demands MS must register : \");\n\t    // Backoff\n\t\tint backoff=utils.retFour(bits,36);\n\t\tsb1.append(\"Backoff=\"+Integer.toString(backoff));\n\t\tdisplay[1]=sb1.toString();\n\t\t// Physical Channel Number\n\t\tint chanNo=utils.retTwelve(bits,44);\n\t\tsb2.append(\"Physical Channel Number \"+Integer.toString(chanNo)+\" : \");\n\t\t// MS Individual Address\n\t\tint msi=utils.retAddress(bits,56);\n\t\tsb2.append(\"MS Individual Address \"+Integer.toString(msi));\n\t\tdisplay[2]=sb2.toString();\n\t}\n\t\n\t\n\t// Return a ACK reason string\n\tprivate String getAckReason (int ack_type,int ack_num)\t{\n\t\t// ACK\n\t\tif (ack_type==1)\t{\n\t\t\tif (ack_num==0b01100000) return \"Message_Accepted\";\n\t\t\telse if (ack_num==0b01100001) return \"Store_Forward\";\n\t\t\telse if (ack_num==0b01100010) return \"Reg_Accepted\";\n\t\t\telse if (ack_num==0b01100011) return \"Accepted for the Status Polling Service\";\n\t\t\telse if (ack_num==0b01000100) return \"MS_Accepted\";\n\t\t\telse if (ack_num==0b01000101) return \"CallBack\";\n\t\t\telse if (ack_num==0b01000110) return \"MS_ALERTING\";\n\t\t\telse if (ack_num==0b01000111) return \"Accepted for the Status Polling Service\";\n\t\t}\n\t\t// NACK\n\t\telse if (ack_type==0)\t{\n\t\t\tif (ack_num==0b00100000) return \"Not_Supported\";\n\t\t\telse if (ack_num==0b00100001) return \"Perm_User_Refused\";\n\t\t\telse if (ack_num==0b00100010) return \"Temp_User_Refused\";\n\t\t\telse if (ack_num==0b00100011) return \"Transient_Sys_Refused\";\n\t\t\telse if (ack_num==0b00100100) return \"NoregMSaway_Refused\";\n\t\t\telse if (ack_num==0b00100101) return \"MSaway_Refused\";\n\t\t\telse if (ack_num==0b00100110) return \"Div_Cause_Fail\";\n\t\t\telse if (ack_num==0b00100111) return \"SYSbusy_Refused\";\n\t\t\telse if (ack_num==0b00101000) return \"SYS_NotReady\";\n\t\t\telse if (ack_num==0b00101001) return \"Call_Cancel_Refused\";\n\t\t\telse if (ack_num==0b00101010) return \"Reg_Refused\";\n\t\t\telse if (ack_num==0b00101011) return \"Reg_Denied\";\n\t\t\telse if (ack_num==0b00101100) return \"IP_Connection_failed\";\n\t\t\telse if (ack_num==0b00101101) return \"MS_Not_Registered\";\n\t\t\telse if (ack_num==0b00101110) return \"Called_Party_Busy\";\n\t\t\telse if (ack_num==0b00000000) return \"MSNot_Supported\";\n\t\t\telse if (ack_num==0b00010001) return \"LineNot_Supported\";\n\t\t\telse if (ack_num==0b00010010) return \"StackFull_Refused\";\t\t\t\n\t\t\telse if (ack_num==0b00010011) return \"EuipBusy_Refused\";\n\t\t\telse if (ack_num==0b00010100) return \"Recipient_Refused\";\n\t\t\telse if (ack_num==0b00010101) return \"Custom_Refused\";\n\t\t}\n\t\t// QACK\n\t\telse if (ack_type==2)\t{\n\t\t\tif (ack_num==0b10100000) return \"Queued-for-resource\";\n\t\t\telse if (ack_num==0b10100001) return \"Queued-for-busy\";\n\t\t}\n\t\t// WACK\n\t\telse if (ack_type==3)\t{\n\t\t\tif (ack_num==0b11100000) return \"Wait\";\n\t\t}\n\t\treturn \"Unknown (at=\"+Integer.toString(ack_type)+\" + ack_num=\"+Integer.toString(ack_num)+\")\";\n\t}\n\t\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/CSVFileFilter.java",
    "content": "package com.dmr;\n\nimport java.io.File;\n\npublic class CSVFileFilter extends javax.swing.filechooser.FileFilter {\n\t\n\tpublic boolean accept(File f) {\n\t\t// if it is a directory -- we want to show it so return true.\n\t\tif (f.isDirectory())\n\t\t\treturn true;\n\t\t// get the extension of the file\n\t\tString extension = getExtension(f);\n\t\t// check to see if the extension is equal to \"csv\"\n\t\tif (extension.equals(\"csv\"))\n\t\t\treturn true;\n\t\t// default -- fall through. False is return on all\n\t\t// occasions except:\n\t\t// a) the file is a directory\n\t\t// b) the file's extension is what we are looking for.\n\t\treturn false;\n\t}\n\n\t/**\n\t * Again, this is declared in the abstract class The description of this\n\t * filter\n\t */\n\tpublic String getDescription() {\n\t\treturn \"CSV files\";\n\t}\n\n\t/**\n\t * Method to get the extension of the file, in lowercase\n\t */\n\tprivate String getExtension(File f) {\n\t\tString s=f.getName();\n\t\tint i=s.lastIndexOf('.');\n\t\tif (i>0&&i<s.length()-1) return s.substring(i+1).toLowerCase();\n\t\telse return \"\";\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/DMRData.java",
    "content": "package com.dmr;\n\npublic class DMRData {\n\tprivate String display[]=new String[3];\n\tprivate DMRDecode theApp;\n\t\n\tpublic DMRData (DMRDecode tapp)\t{\n\t\ttheApp=tapp;\n\t}\n\t\n\t// The header decode method\n\tpublic String[] decodeHeader (boolean bits[])\t{\n\t\tint dpf;\n\t\t// Data Packet Format\n\t\tif (bits[4]==true) dpf=8;\n\t\telse dpf=0;\n\t\tif (bits[5]==true) dpf=dpf+4;\n\t\tif (bits[6]==true) dpf=dpf+2;\n\t\tif (bits[7]==true) dpf++;\n\t\t// Types\n\t\tif (dpf==0) udt(bits);\n\t\telse if (dpf==1) responsePacket(bits);\n\t\telse if (dpf==2) unconfirmedData(bits);\n\t\telse if (dpf==3) confirmedData(bits);\n\t\telse if (dpf==13) definedShortData(bits);\n\t\telse if (dpf==14) rawShortData(bits);\n\t\telse if (dpf==15) propData(bits);\n\t\telse unknownData(bits,dpf);\n\t\treturn display;\n\t}\n\t\n\t// Decode a half rate packet\n\tpublic String[] decodeHalfRate (boolean bits[])\t{\n\t\t// Increment the blocks received counter\n\t\ttheApp.incrementCurrentDataBlocksReceived();\n\t\t// Depending on the data type handle this in different ways\n\t\tif (theApp.getCurrentIncomingDataType()==2)\t{\n\t\t\thandleConfirmedData(bits);\n\t\t\treturn display;\n\t\t}\n\t\t\n\t\t// TODO : Handle other types of rate 1/2 data\n\t\t\n\t\t// Just display the rest as binary for now\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tint a;\n\t\tfor (a=0;a<bits.length;a++)\t{\n\t\t\tif (bits[a]==true) sb.append(\"1\");\n\t\t\telse sb.append(\"0\");\n\t\t}\n\t\tdisplay[0]=sb.toString();\n\t\t\n\t\treturn display;\n\t}\n\t\n\t// Decode a three quarter rate packet\n\tpublic String[] decodeThreeQuarterRate (boolean bits[])\t{\n\t\t// Create a Trellis object\n\t\tTrellis trellis=new Trellis();\n\t\tboolean threeQuarterOut[]=trellis.decode(bits);\n\t\t// Increment the blocks received counter\n\t\ttheApp.incrementCurrentDataBlocksReceived();\n\t\t// If this is null then there is an error so return null\n\t\tif (threeQuarterOut==null) return null;\n\t\t// Depending on the data type handle this in different ways\n\t\tif (theApp.getCurrentIncomingDataType()==2)\t{\n\t\t\thandleConfirmedData(threeQuarterOut);\n\t\t\treturn display;\n\t\t}\n\t\t\n\t\t// TODO : Handle other types of rate 3/4 data\n\t\t\n\t\t// Just display the rest as binary for now\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tint a;\n\t\tfor (a=0;a<threeQuarterOut.length;a++)\t{\n\t\t\tif (threeQuarterOut[a]==true) sb.append(\"1\");\n\t\t\telse sb.append(\"0\");\n\t\t}\n\t\tdisplay[0]=sb.toString();\n\t\treturn display;\n\t}\n\t\n\t// Unified Data Transport\n\tprivate void udt (boolean bits[])\t{\n\t\tdisplay[0]=\"Unified Data Transport\";\n\t\t// Set the data type\n\t\ttheApp.setCurrentIncomingDataType(8);\n\t}\n\t\n\t// Response Packet\n\tprivate void responsePacket (boolean bits[])\t{\n\t\tint blocks,dclass,status,type;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sa=new StringBuilder(250);\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tdisplay[0]=\"Response Packet\";\n\t\t// Destination LLID\n\t\tint dllid=utils.retAddress(bits,16);\n\t\t// Source LLID\n\t\tint sllid=utils.retAddress(bits,40);\n\t\tsa.append(\"Destination Logical Link ID : \"+Integer.toString(dllid));\n\t\tsa.append(\" Source Logical Link ID : \"+Integer.toString(sllid));\n\t\tdisplay[1]=sa.toString();\n\t\t// Bit 64 is 0\n\t\t// Blocks to follow\n\t\tif (bits[65]==true) blocks=64;\n\t\telse blocks=0;\n\t\tif (bits[66]==true) blocks=blocks+32;\n\t\tif (bits[67]==true) blocks=blocks+16;\n\t\tif (bits[68]==true) blocks=blocks+8;\n\t\tif (bits[69]==true) blocks=blocks+4;\n\t\tif (bits[70]==true) blocks=blocks+2;\n\t\tif (bits[71]==true) blocks++;\n\t\t// Class\n\t\tif (bits[72]==true) dclass=2;\n\t\telse dclass=0;\n\t\tif (bits[73]==true) dclass++;\n\t\t// Type\n\t\tif (bits[74]==true) type=4;\n\t\telse type=0;\n\t\tif (bits[75]==true) type=type+2;\n\t\tif (bits[76]==true) type++;\n\t\t// Status\n\t\tif (bits[77]==true) status=4;\n\t\telse status=0;\n\t\tif (bits[78]==true) status=status+2;\n\t\tif (bits[79]==true) status++;\n\t\t// Set the data type\n\t\ttheApp.setCurrentIncomingDataType(3);\n\t\t// Set the number of blocks to follow\n\t\ttheApp.setCurrentDataBlocksToFollow(blocks);\n\t\t// Display this\n\t\tsb.append(Integer.toString(blocks)+\" blocks follow : \");\n\t\tif ((dclass==0)&&(type==1)) sb.append(\"ACK\");\n\t\telse if ((dclass==1)&&(type==0)) sb.append(\"NACK (Illegal Format)\");\n\t\telse if ((dclass==1)&&(type==1)) sb.append(\"NACK (CRC Failed)\");\n\t\telse if ((dclass==1)&&(type==2)) sb.append(\"NACK (Memory Full)\");\n\t\telse if ((dclass==1)&&(type==4)) sb.append(\"NACK (Undeliverable)\");\n\t\telse if ((dclass==2)&&(type==0)) sb.append(\"SACK\");\n\t\telse sb.append(\" Unknown C=\"+Integer.toString(dclass)+\" T=\"+Integer.toString(type)+\" S=\"+Integer.toString(status));\n\t\tdisplay[2]=sb.toString();\n\t}\n\t\n\t// Unconfirmed Data\n\tprivate void unconfirmedData (boolean bits[])\t{\n\t\tint blocks,fsn;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sa=new StringBuilder(250);\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tdisplay[0]=\"Unconfirmed Data\";\n\t\t// Destination LLID\n\t\tint dllid=utils.retAddress(bits,16);\n\t\t// Source LLID\n\t\tint sllid=utils.retAddress(bits,40);\n\t\tsa.append(\"Destination Logical Link ID : \"+Integer.toString(dllid));\n\t\tsa.append(\" Source Logical Link ID : \"+Integer.toString(sllid));\n\t\tdisplay[1]=sa.toString();\n\t\t// Bit 64 is 0\n\t\t// Blocks to follow\n\t\tif (bits[65]==true) blocks=64;\n\t\telse blocks=0;\n\t\tif (bits[66]==true) blocks=blocks+32;\n\t\tif (bits[67]==true) blocks=blocks+16;\n\t\tif (bits[68]==true) blocks=blocks+8;\n\t\tif (bits[69]==true) blocks=blocks+4;\n\t\tif (bits[70]==true) blocks=blocks+2;\n\t\tif (bits[71]==true) blocks++;\n\t\t// Bits 72,73,74 and 75 are 0\n\t\t// FSN\n\t\tif (bits[76]==true) fsn=8;\n\t\telse fsn=0;\n\t\tif (bits[77]==true) fsn=fsn+4;\n\t\tif (bits[78]==true) fsn=fsn+2;\n\t\tif (bits[79]==true) fsn++;\n\t\t// Set the data type\n\t\ttheApp.setCurrentIncomingDataType(1);\n\t\t// Set the blocks to follow\n\t\ttheApp.setCurrentDataBlocksToFollow(blocks);\n\t\t// Display this\n\t\tsb.append(Integer.toString(blocks)+\" blocks follow : FSN=\"+Integer.toString(fsn));\n\t\tdisplay[2]=sb.toString();\n\t}\n\t\n\t// Confirmed Data\n\tprivate void confirmedData (boolean bits[])\t{\n\t\tint blocks,fsn,ns;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sa=new StringBuilder(250);\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tdisplay[0]=\"Confirmed Data\";\n\t\t// Destination LLID\n\t\tint dllid=utils.retAddress(bits,16);\n\t\t// Source LLID\n\t\tint sllid=utils.retAddress(bits,40);\n\t\tsa.append(\"Destination Logical Link ID : \"+Integer.toString(dllid));\n\t\tsa.append(\" Source Logical Link ID : \"+Integer.toString(sllid));\n\t\tdisplay[1]=sa.toString();\n\t\t// Bit 64 is F\n\t\t// Blocks to follow\n\t\tif (bits[65]==true) blocks=64;\n\t\telse blocks=0;\n\t\tif (bits[66]==true) blocks=blocks+32;\n\t\tif (bits[67]==true) blocks=blocks+16;\n\t\tif (bits[68]==true) blocks=blocks+8;\n\t\tif (bits[69]==true) blocks=blocks+4;\n\t\tif (bits[70]==true) blocks=blocks+2;\n\t\tif (bits[71]==true) blocks++;\n\t\t// Bits 72 is S\n\t\t// Bits 73,74,75 are N(S)\n\t\tif (bits[73]==true) ns=4;\n\t\telse ns=0;\n\t\tif (bits[74]==true) ns=ns+2;\n\t\tif (bits[75]==true) ns++;\n\t\t// FSN\n\t\tif (bits[76]==true) fsn=8;\n\t\telse fsn=0;\n\t\tif (bits[77]==true) fsn=fsn+4;\n\t\tif (bits[78]==true) fsn=fsn+2;\n\t\tif (bits[79]==true) fsn++;\n\t\t// Set the data type\n\t\ttheApp.setCurrentIncomingDataType(2);\n\t\t// Set the blocks to follow\n\t\ttheApp.setCurrentDataBlocksToFollow(blocks);\n\t\t// Display this\n\t\tsb.append(Integer.toString(blocks)+\" blocks follow : FSN=\"+Integer.toString(fsn)+\" N(S)=\"+Integer.toString(ns));\n\t\tdisplay[2]=sb.toString();\t\t\n\t}\n\t\n\t// Defined Short Data\n\tprivate void definedShortData (boolean bits[])\t{\n\t\tdisplay[0]=\"Defined Short Data\";\n\t\t// Set the data type\n\t\ttheApp.setCurrentIncomingDataType(7);\n\t}\n\t\n\t// Raw Short Data\n\tprivate void rawShortData (boolean bits[])\t{\n\t\tdisplay[0]=\"Raw or Status Short Data\";\n\t\t// Set the data type\n\t\ttheApp.setCurrentIncomingDataType(6);\n\t}\n\t\n\t// Proprietary Data Packet\n\tprivate void propData (boolean bits[])\t{\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sa=new StringBuilder(250);\n\t\tint mfid=utils.retEight(bits,8);\n\t\tdisplay[0]=\"Proprietary Data : MFID=\"+Integer.toString(mfid)+\" (\"+utils.returnMFIDName(mfid)+\")\";\n\t\t// Display proprietary data as binary\n\t\tint a;\n\t\tfor (a=16;a<80;a++)\t{\n\t\t\tif (bits[a]==true) sa.append(\"1\");\n\t\t\telse sa.append(\"0\");\n\t\t}\n\t\tdisplay[1]=sa.toString();\n\t\t// Set the data type\n\t\ttheApp.setCurrentIncomingDataType(4);\t\n\t}\n\t\n\t\n\t// Unknown Data\n\tprivate void unknownData (boolean bits[],int dpf)\t{\n\t\tdisplay[0]=\"Unknown Data : DPF=\"+Integer.toString(dpf);\n\t}\n\t\n\t// Handle confirmed data \n\tprivate void handleConfirmedData (boolean bits[])\t{\n\t\tint dbsn,crc;\n\t\t// Data block serial number \n\t\t// bits 0,1,2,3,4,5,6\n\t\tif (bits[0]==true) dbsn=64;\n\t\telse dbsn=0;\n\t\tif (bits[1]==true) dbsn=dbsn+32;\n\t\tif (bits[2]==true) dbsn=dbsn+16;\n\t\tif (bits[3]==true) dbsn=dbsn+8;\n\t\tif (bits[4]==true) dbsn=dbsn+4;\n\t\tif (bits[5]==true) dbsn=dbsn+2;\n\t\tif (bits[6]==true) dbsn++;\n\t\t// 9 bit CRC\n\t\t// bits 7,8,9,10,11,12,13,14,15\n\t\tif (bits[7]==true) crc=256;\n\t\telse crc=0;\n\t\tif (bits[8]==true) crc=crc+128;\n\t\tif (bits[9]==true) crc=crc+64;\n\t\tif (bits[10]==true) crc=crc+32;\n\t\tif (bits[11]==true) crc=crc+16;\n\t\tif (bits[12]==true) crc=crc+8;\n\t\tif (bits[13]==true) crc=crc+4;\n\t\tif (bits[14]==true) crc=crc+2;\n\t\tif (bits[15]==true) crc++;\n\t\t// If 96 bits this is R_1_2_DATA\n\t\tif (bits.length==96) display[0]=\"R_1_2_DATA (data block serial number=\"+Integer.toString(dbsn)+\")\";\n\t\telse if (bits.length==144) display[0]=\"R_3_4_DATA (data block serial number=\"+Integer.toString(dbsn)+\")\";\n\t\t// Display the payload as binary for now\n\t\tint a;\n\t\tStringBuilder sb=new StringBuilder(150);\n\t\tfor (a=16;a<bits.length;a++)\t{\n\t\t\tif (bits[a]==true) sb.append(\"1\");\n\t\t\telse sb.append(\"0\");\n\t\t}\n\t\tdisplay[1]=sb.toString();\n\t}\n\t\t\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/DMRDataDecode.java",
    "content": "package com.dmr;\n\nimport java.awt.Color;\nimport java.awt.Font;\n\npublic class DMRDataDecode {\n\tprivate int dataType=-1;\n\tprivate String line[]=new String[10];\n\tprivate Font fonts[]=new Font[10];\n\tprivate Color colours[]=new Color[10];\n\tprivate boolean CACHres,SLOT_TYPEres,BPTCres;\n\tprivate boolean shouldDisplay=true;\n\t\n\tpublic String[] decode (DMRDecode theApp,byte[] dibit_buf)\t{\n\t\tString cline;\n\t\tSlotType slottype=new SlotType();\n\t\tint mode=theApp.getMode();\n\t\tDecodeCACH cachdecode=new DecodeCACH();\n\t\tif (theApp.getSyncType()==40) line[0]=theApp.getTimeStamp()+\" DMR Data Rest Frame\";\n\t\telse line[0]=theApp.getTimeStamp()+\" DMR Data Frame\";\n\t\tif (mode==0) line[0]=line[0]+\" (BS)\";\n\t\telse if (mode==1) line[0]=line[0]+\" (MS)\";\n\t\telse if (mode==2) line[0]=line[0]+\" (Direct)\";\n\t\tcolours[0]=Color.BLACK;\n\t\tfonts[0]=theApp.boldFont;\n\t\t// CACH decode\n\t\tif (mode==0)\t{\n\t\t\tcline=cachdecode.decode(theApp,dibit_buf);\n\t\t\tCACHres=cachdecode.isPassErrorCheck();\n\t\t}\n\t\telse\t{\n\t\t\tCACHres=true;\n\t\t\tcline=null;\n\t\t}\n\t\t// Slot Type Decode\n\t\tif (CACHres==true)\t{\n\t\t\tif (mode==0)\t{\n\t\t\t\tline[1]=cline;\n\t\t\t\tfonts[1]=theApp.italicFont;\n\t\t\t\tcolours[1]=Color.BLACK;\n\t\t\t}\n\t\t\tline[2]=slottype.decode(theApp,dibit_buf);\n\t\t\tSLOT_TYPEres=slottype.isPassErrorCheck();\n\t\t\t// If short LC data is available then display it\n\t\t\tif ((cachdecode.getShortLC()==true)&&(mode==0))\t{\n\t\t\t\tline[7]=cachdecode.getShortLCline();\n\t\t\t\tfonts[7]=theApp.boldFont;\n\t\t\t\tif (cachdecode.getshortLCError()==true)\t{\n\t\t\t\t\tcolours[7]=Color.RED;\n\t\t\t\t\tif (theApp.isDisplayOnlyGoodFrames()==true) line[7]=null;\n\t\t\t\t}\n\t\t\t\telse colours[7]=Color.BLACK;\n\t\t\t\tcachdecode.clearShortLC();\n\t\t\t}\n\t\t\tif (SLOT_TYPEres==true)\t{\n\t\t\t\tfonts[2]=theApp.boldFont;\n\t\t\t\tcolours[2]=Color.BLACK;\n\t\t\t\t// If no error then get the data type\n\t\t\t\tdataType=slottype.returnDataType();\n\t\t\t\t// Main section decode\n\t\t\t\t// PI Header\n\t\t\t\tif (dataType==0)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\t// Display the PI header bits as raw binary\n\t\t\t\t\t\tStringBuilder sb=new StringBuilder();\n\t\t\t\t\t\tint ai;\n\t\t\t\t\t\tfor (ai=0;ai<bits.length;ai++)\t{\n\t\t\t\t\t\t\tif (bits[ai]==true) sb.append(\"1\");\n\t\t\t\t\t\t\telse sb.append(\"0\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tline[3]=sb.toString();\n\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Voice LC Header\n\t\t\t\telse if (dataType==1)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\t// TODO : Ensure the Voice LC Headers in Data Frames pass the Reed Solomon (12,9) error check \n\t\t\t\t\t\tFullLinkControl flc=new FullLinkControl();\n\t\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\t\tclines=flc.decode(theApp,bits);\n\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\tline[5]=clines[2];\n\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Terminator with LC\n\t\t\t\telse if (dataType==2)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\t// TODO : Ensure the Terminator LCs in Data Frames pass the Reed Solomon (12,9) error check \n\t\t\t\t\t\tFullLinkControl flc=new FullLinkControl();\n\t\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\t\tclines=flc.decode(theApp,bits);\n\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\tline[5]=clines[2];\n\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// CSBK\n\t\t\t\telse if (dataType==3)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tcrc tCRC=new crc();\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\t// Does the CSBK pass its CRC test ?\n\t\t\t\t\t\tif (tCRC.crcCSBK(bits)==true)\t{\n\t\t\t\t\t\t\tCSBK csbk=new CSBK();\n\t\t\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\t\tclines=csbk.decode(theApp,bits);\n\t\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\t\tline[5]=clines[2];\n\t\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Data Header\n\t\t\t\telse if (dataType==6)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tcrc tCRC=new crc();\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\t// Does the Data Header pass its CRC test ?\n\t\t\t\t\t\tif (tCRC.crcDataHeader(bits)==true)\t{\n\t\t\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\t\tDMRData data=new DMRData(theApp);\n\t\t\t\t\t\t\tclines=data.decodeHeader(bits);\n\t\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\t\tline[5]=clines[2];\n\t\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t\t}\t\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Rate  Data Continuation\n\t\t\t\telse if (dataType==7) {\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\t\tDMRData data=new DMRData(theApp);\n\t\t\t\t\t\tclines=data.decodeHalfRate(bits);\n\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\tline[5]=clines[2];\n\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Rate  Data Continuation\n\t\t\t\telse if (dataType==8)\t{\n\t\t\t\t\t// Just use the BPTC19696 class to extract the raw binary from the dibit buffer\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tboolean bits[]=bptc19696.rawOut(dibit_buf);\n\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\tDMRData data=new DMRData(theApp);\n\t\t\t\t\tclines=data.decodeThreeQuarterRate(bits);\n\t\t\t\t\t// If clines is null then we have an error\n\t\t\t\t\tif (clines==null)\t{\n\t\t\t\t\t\tBPTCres=false;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\tline[5]=clines[2];\n\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Idle\n\t\t\t\t// Error check this to detect problems with the data stream\n\t\t\t\telse if (dataType==9)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tBPTCres=bptc19696.decode(dibit_buf);\n\t\t\t\t\t// If we don't want to display these then clear the lines\n\t\t\t\t\tif (theApp.isDisplayIdlePDU()==false) shouldDisplay=false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\n\t\t\t\n\t\t}\n\t\t\n\t\ttheApp.frameCount++;\n\t\treturn line;\n\t}\n\n\t// Inform the main class that there has been an error\n\tpublic boolean isError() {\n\t  if ((SLOT_TYPEres==true)&&(CACHres==true)&&(BPTCres==true)) return true;\n\t  else return false;\n\t}\n\t\n\t// Return the fonts in use\n\tpublic Font[] getFonts()\t{\n\t\treturn fonts;\n\t}\n\t\n\t// Return the colours in use\n\tpublic Color[] getColours()\t{\n\t\treturn colours;\n\t}\n\t\n\t// Tells the main program if this PDU should be displayed\n\tpublic boolean getShouldDisplay()\t{\n\t\treturn shouldDisplay;\n\t}\n\t\n\tpublic void setShouldDisplay (boolean b)\t{\n\t\tshouldDisplay=b;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/DMRDecode.java",
    "content": "// Please note much of the code in this program was taken from the DSD software\n// and converted into Java. The author of this software is unknown but has the\n// GPG Key ID below\n\n// Copyright (C) 2010 DSD Author\n// GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6  F630 FAA2 635D 3F1D 7FD0)\n// \n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND ISC DISCLAIMS ALL WARRANTIES WITH\n// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,\n// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE\n// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n// PERFORMANCE OF THIS SOFTWARE.\n//\n\npackage com.dmr;\n\nimport java.awt.*;\nimport java.awt.event.WindowEvent;\nimport java.awt.event.WindowAdapter;\nimport java.io.FileWriter;\nimport javax.swing.*;\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.parsers.SAXParser;\nimport javax.xml.parsers.SAXParserFactory;\nimport org.xml.sax.Attributes;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.helpers.DefaultHandler;\nimport java.text.DateFormat;\nimport java.util.Date;\nimport java.io.DataInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PipedInputStream;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.ThreadFactory;\n\npublic class DMRDecode {\n\tprivate DisplayModel display_model;\n\tprivate DisplayView display_view;\n\tprivate static DMRDecode theApp;\n\tprivate static DisplayFrame window;\n\tpublic String program_version=\"DMR Decoder (Build 74)\";\n\tpublic int vertical_scrollbar_value=0;\n\tpublic int horizontal_scrollbar_value=0;\n\tprivate static boolean RUNNING=true;\n\tprivate final int SAMPLESPERSYMBOL=10;\n\tprivate final int SYMBOLCENTRE=4;\n\tprivate final int MAXSTARTVALUE=15000;\n\tprivate final int MINSTARTVALUE=-15000;\n\tprivate int max=MAXSTARTVALUE;\n\tprivate int min=MINSTARTVALUE;\n\tprivate int centre=0;\n\tprivate int lastsynctype=-1;\n\tprivate int symbolcnt=0;\n\tprivate final byte DMR_DATA_SYNC_BS[]={3,1,3,3,3,3,1,1,1,3,3,1,1,3,1,1,3,1,3,3,1,1,3,1};\n\tprivate final byte DMR_VOICE_SYNC_BS[]={1,3,1,1,1,1,3,3,3,1,1,3,3,1,3,3,1,3,1,1,3,3,1,3};\n\tprivate final byte DMR_DATA_SYNC_MS[]={3,1,1,1,3,1,1,3,3,3,1,3,1,3,3,3,3,1,1,3,1,1,1,3};\n\tprivate final byte DMR_VOICE_SYNC_MS[]={1,3,3,3,1,3,3,1,1,1,3,1,3,1,1,1,1,3,3,1,3,3,3,1};\n\tprivate final byte DMR_RC_SYNC[]={1,3,1,3,3,1,1,1,1,1,3,3,1,3,3,1,3,3,3,1,1,3,1,3};\n\tprivate final byte DMR_DATA_SYNC_DIRECT1[]={3,3,1,3,3,3,3,1,3,1,1,1,3,1,3,1,3,3,3,1,1,1,1,1};\n\tprivate final byte DMR_VOICE_SYNC_DIRECT1[]={1,1,3,1,1,1,1,3,1,3,3,3,1,3,1,3,1,1,1,3,3,3,3,3};\n\tprivate final byte DMR_DATA_SYNC_DIRECT2[]={3,1,1,3,1,1,1,1,1,3,3,3,1,1,3,3,3,3,1,3,3,3,1,1};\n\tprivate final byte DMR_VOICE_SYNC_DIRECT2[]={1,3,3,1,3,3,3,3,3,1,1,1,3,3,1,1,1,1,3,1,1,1,3,3};\n\tprivate final byte DMR_DATA_REST_SYNC_BS[]={3,1,3,1,1,3,3,3,3,3,1,1,3,1,1,3,1,1,1,3,3,1,3,1};\n\tprivate boolean carrier=false;\n\tpublic boolean inverted=true;\n\tprivate boolean firstframe=false;\n\tprivate int lmid=0;\n\tprivate int umid=0;\n\tprivate int synctype;\n\tprivate byte dibitCircularBuffer[]=new byte[144];\n\tprivate int dibitCircularBufferCounter=0;\n\tprivate byte dibitFrame[]=new byte[144];\n\tprivate boolean frameSync=false;\n\tpublic FileWriter file;\n\tpublic FileWriter captureFile;\n\tprivate boolean logging=false;\n\tpublic boolean pReady=false;\n\tprivate int symbolBuffer[]=new int[144];\n\tpublic AudioInThread lineInThread=new AudioInThread(this);\n\tprivate boolean debug=false;\n\tpublic int frameCount=0;\n\tpublic int badFrameCount=0;\n\tpublic ShortLC short_lc=new ShortLC();\n\tpublic EmbeddedLC embedded_lc=new EmbeddedLC();\n\tpublic int embeddedFrameCount=0;\n\tprivate int symbolBufferCounter=0;\n\tprivate int errorFreeFrameCount=0;\n\tprivate int continousBadFrameCount=0;\n\tprivate boolean captureMode=false;\n\tprivate long captureCount=0;\n\tprivate boolean enableDisplayBar=false;\n\tprivate final int SYMBOLSAHEAD=144;\n\tprivate final int SAMPLESAHEADSIZE=(SYMBOLSAHEAD*SAMPLESPERSYMBOL)+SAMPLESPERSYMBOL;\n\tprivate int samplesAheadBuffer[]=new int[SAMPLESAHEADSIZE];\n\tprivate int samplesAheadCounter=0;\n\tprivate int jitter=-1;\n\tprivate DataInputStream inPipeData;\n\tprivate PipedInputStream inPipe;\n\tprivate int lastSample=0;\n\tprivate final int JITTERFRAMEADJUST=1;\n\tprivate final int JITTERCOUNTERSIZE=(JITTERFRAMEADJUST*144);\n\tprivate int jitterCounter=0;\n\tprivate int jitterBuffer[]=new int[JITTERCOUNTERSIZE];\n\tprivate int syncHighLowlBuf[]=new int[24];\n\tpublic UsersLogged usersLogged=new UsersLogged();\n\tprivate final int MAXMINBUFSIZE=5;\n\tprivate int maxminBufferCounter=0;\n\tprivate int maxBuffer[]=new int[MAXMINBUFSIZE];\n\tprivate int minBuffer[]=new int[MAXMINBUFSIZE];\n\tpublic final Font plainFont=new Font(\"SanSerif\",Font.PLAIN,12);\n\tpublic final Font boldFont=new Font(\"SanSerif\",Font.BOLD,12);\n\tpublic final Font italicFont=new Font(\"SanSerif\",Font.ITALIC,12);\n\tpublic SocketOut socketThread=new SocketOut(this);\n\tpublic int currentChannel=0;\n\tprivate boolean displayCACH=true;\n\tprivate boolean displayIdlePDU=true;\n\tprivate boolean displayOnlyGoodFrames=false;\n\tprivate boolean displayVoiceFrames=true;\n\tpublic final Color labelBusyColour=Color.BLACK;\n\tpublic final Color labelQuiteColour=Color.GRAY;\n\tprivate boolean pauseScreen=false;\n\tprivate boolean quickLog=false;\n\tpublic FileWriter quickLogFile;\n\tprivate int colourCode=0;\n    private int socketThreadPriority=3;\n    private int audioInputPriority=3;\n    private int mainThreadPriority=5;\n    private int incomingDataType[]=new int[2];\n    private int dataBlocksToFollow[]=new int[2];\n    private int dataBlocksReceived[]=new int[2];\n    private int mode=-1;\n    \n    private ExecutorService socketExecutor = Executors.newSingleThreadExecutor(\n        new ThreadFactory(){\n            public Thread newThread(Runnable r){\n                Thread thread=new Thread(r);\n                thread.setName(\"DMRDecode Socket Thread\");\n                thread.setPriority(socketThreadPriority);\n                return thread;\n            }\n        }\n    );\n\n    private ExecutorService mainExecutor = Executors.newSingleThreadExecutor(\n        new ThreadFactory(){\n            public Thread newThread(Runnable r){\n                Thread thread=new Thread(r);\n                thread.setName(\"DMRDecode Main Thread\");\n                thread.setPriority(mainThreadPriority);\n                return thread;\n            }\n        }\n    );\n\n    private ExecutorService audioInputExecutor = Executors.newSingleThreadExecutor(\n        new ThreadFactory(){\n            public Thread newThread(Runnable r){\n                Thread thread=new Thread(r);\n                thread.setName(\"DMRDecode Audio Input Thread\");\n                thread.setPriority(audioInputPriority);\n                return thread;\n            }\n        }\n    );\n\n\t\n\tpublic static void main(String[] args) {\n\t\t// Setup the TCP/IP socket code\n\t\ttry\t{\n            theApp=new DMRDecode();\n            SwingUtilities.invokeAndWait(new Runnable(){public void run(){theApp.createGUI();}});\n            theApp.short_lc.setApp(theApp);\n            theApp.socketExecutor.submit(theApp.socketThread);\n            //this returns a boolean...\n\t\t\ttheApp.socketThread.setupSocket();\t\t\t\n\t\t} catch (Exception e)\t{\n\t\t\tJOptionPane.showMessageDialog(null,\"Error in socket setup during main()\",\"DMRDecode\", JOptionPane.INFORMATION_MESSAGE);\n\t\t\tSystem.exit(0);\n\t\t}\n\t\t// Get data from the soundcard thread\n\t\ttry\t{\n\t\t\t// Start the audio thread\n\t\t\ttheApp.lineInThread.startAudio();\n\t\t\t// Connected a piped input stream to the piped output stream in the thread\n\t\t\ttheApp.inPipe=new PipedInputStream(theApp.lineInThread.getPipedWriter(), 16384);\n\t\t\t// Now connect a data input stream to the piped input stream\n\t\t\ttheApp.inPipeData=new DataInputStream(theApp.inPipe);\n        }\n\t\tcatch (Exception e)\t{\n\t\t\tJOptionPane.showMessageDialog(null,\"Error in main()\",\"DMRDecode\", JOptionPane.INFORMATION_MESSAGE);\n\t\t\tSystem.exit(0);\n        }\n\n        theApp.audioInputExecutor.submit(theApp.lineInThread);\n        \n        theApp.mainExecutor.submit(new Runnable(){\n            public void run(){\n                while (RUNNING)\t{\n                    if ((theApp.lineInThread.getAudioReady()==true)&&(theApp.pReady==true)) theApp.decode();\n                }\n            }\n        });\n\n    }\n\t\n\t// Setup the window //\n\tpublic void createGUI() {\n\t\twindow=new DisplayFrame(program_version,this);\n\t\tToolkit theKit=window.getToolkit();\n\t\tDimension wndsize=theKit.getScreenSize();\n\t\twindow.setBounds(wndsize.width/6,wndsize.height/6,2*wndsize.width/3,2*wndsize.height/3);\n\t\twindow.addWindowListener(new WindowHandler());\n\t\tdisplay_model=new DisplayModel();\n\t\tdisplay_view=new DisplayView(this);\n\t\tdisplay_model.addObserver(display_view);\n\t\twindow.getContentPane().add(display_view,BorderLayout.CENTER);\n\t\twindow.setVisible(true);\n\t\t// Make certain the program knows the GUI is ready\n\t\tpReady=true;\n    }\n\n\tclass WindowHandler extends WindowAdapter {\n\t\tpublic void windowClosing(WindowEvent e) {\t\n        }\n    }\n\n\tpublic DisplayFrame getWindow()\t{\n\t\treturn window;\t\n    }\n\n\tpublic DisplayModel getModel() {\n\t\treturn display_model;\n    }\n\n\tpublic DisplayView getView() {\n\t\treturn display_view;\t\n    }\n\t\n\t// The main routine for decoding DMR data\n\tpublic void decode()\t{\n\t\t  noCarrier();\n\t\t  synctype=getFrameSync();\n\t      while (synctype!=-1)\t{\n\t          processFrame();\n\t          synctype=getFrameSync(); \n\t          createDibitFrame();\n\t        }  \n\t  }\n\t\n\n\t// A function containing the calculations required when a frame is detected\n\tprivate void frameCalcs (int lmin,int lmax)\t{\n\t\t// The code required below appears to depend on the soundcard\n\t\t// Viglen PC code\n\t\t//max=(lmax+max)/2;\n\t\t//min=(lmin+min)/2;\t\n\t\t// Acer PC Code \n\t\tmax=lmax;\n\t\tmin=lmin;\n\t\tcentre=(max+min)/2;\n\t\tumid=(int)((float)(max-centre)*(float)0.625)+centre;\n\t    lmid=(int)((float)(min-centre)*(float)0.625)+centre;\t\t\n\t    // If debug enabled then record this\n\t\tif (debug==true)\t{\n\t\t\tString l=getTimeStamp()+\" Setting new params : centre=\"+Integer.toString(centre)+\" max=\"+Integer.toString(max)+\" min=\"+Integer.toString(min)+\" umid=\"+Integer.toString(umid)+\" lmid=\"+Integer.toString(lmid);\n\t\t\taddLine(l,Color.BLACK,plainFont);\n\t\t\tfileWrite(l);\n\t\t\t}\n\t    \n\t    // Pass these settings to the display bar\n        window.displayBarParams(max,min,umid,lmid);\n\t}\n\t\n\t// This code lifted straight from the DSD source code converted to Java \n\t// and tidied up removing non DMR code\n\tpublic int getSymbol(boolean have_sync)\t{\n\t\t  int sample,i,sum=0,symbol,count=0;\n\t\t  for (i=0;i<SAMPLESPERSYMBOL;i++)\t{\n\t\t\t  // Fall back or catch up\n\t\t\t  if ((i==0)&&(jitter>0))\t{\n\t\t\t\t  \n\t\t\t\t  if ((frameSync==true)&&(debug==true))\t{\n\t\t\t\t\t  String l=getTimeStamp()+\" jitter change to \"+Integer.toString(jitter);\n\t\t\t\t\t  addLine(l,Color.BLACK,plainFont);\n\t\t\t\t\t  fileWrite(l);\n\t\t\t\t  }\n\t\t\t\t  \n\t\t\t\t  if ((jitter>0)&&(jitter<=SYMBOLCENTRE)) i--;          \n\t\t\t\t  else if ((jitter>SYMBOLCENTRE)&&(jitter<SAMPLESPERSYMBOL)) i++;\n\t\t\t\t  jitter=-1;\n\t\t\t\t  }\n\t\t      // Get the sample from whatever source\n\t\t\t  sample=getSample(false);\t\n\t\t\t  // Jitter adjust code\n\t\t\t  // Is this sample greater than the centre ?\n\t\t\t  if (sample>centre)\t{\n\t\t\t\t  \t  // Was the last sample less than the centre ?\n\t\t\t\t\t  if (lastSample<centre)\t{\n\t\t\t\t\t\t  // Yes we have a zero crossing\n\t\t\t\t\t\t  if (frameSync==false) jitter=i;\n\t\t\t\t\t\t  else processJitter(i);\n\t\t\t\t\t  }\n\t\t\t  }\n\t\t\t  else\t{\n\t\t\t\t  \t  // If this sample is less than the centre then\n\t\t\t\t      // was the last sample greater than the centre\n\t\t\t\t\t  if (lastSample>centre)\t{\n\t\t\t\t\t\t  // Yes we have a zero crossing\n\t\t\t\t\t\t  if (frameSync==false) jitter=i;\n\t\t\t\t\t\t  else processJitter(i);\n\t\t\t\t\t  }\n\t\t\t  }\n\t\t\t  // Sample the symbol from its centre \n\t\t\t  if ((i>=SYMBOLCENTRE)&&(i<=SYMBOLCENTRE+1))\t{\n\t\t\t  \t\t  sum=sum+sample;\n\t\t\t\t\t  count++;\n\t\t\t\t  }\n\t\t      // Make copy of this sample for later comparison\n\t\t      lastSample=sample;\n\t\t    }\n\t\t  symbol=(sum/count);\n\t\t  symbolcnt++;\t\t  \n\t\t  return symbol;\n\t  }\n\t\n\t// Add the calculated jitter value to the jitter circular buffer\n\tprivate void processJitter (int jit)\t{\n\t\tjitterBuffer[jitterCounter]=jit;\n\t\tjitterCounter++;\n\t\tif (jitterCounter==JITTERCOUNTERSIZE)\t{\n\t\t\tjitterCounter=0;\n\t\t\t// Set the jitter to the mode value of the jitter buffer\n\t\t\tjitter=calcJitterMode();\t\t\t\n\t\t}\n\t}\n\t  \n\t// Calculate which jitter value occurs the most (the mode) and return it \n\tprivate int calcJitterMode()\t{\n\t\tint a,b,high=0,highMode=0,tmode;\n\t\tfor (a=0;a<SAMPLESPERSYMBOL;a++)\t{\n\t\t\ttmode=0;\n\t\t\tfor (b=0;b<JITTERCOUNTERSIZE;b++)\t{\n\t\t\t\tif (jitterBuffer[b]==a) tmode++;\n\t\t\t}\n\t\t\tif (tmode>highMode)\t{\n\t\t\t\thigh=a;\n\t\t\t\thighMode=tmode;\n\t\t\t}\n\t\t}\n\t\treturn high;\n\t}\n\t\n\n\t// Grab 144 dibits then check if they have a sync pattern and if they do then process \n\t// them accordingly\n\tpublic int getFrameSync ()\t{\n\t\tint t=0,dibit,symbol,synctest_pos=0,syncType;\n\t\tint lmin=0,lmax=0,a,highVol;\n\t\t// Clear the symbol counter\n\t\tsymbolcnt=0;\n\t\twhile (true) {\n\t\t\tt++;\n\t\t\t// Get a symbol from the soundcard\n\t\t\tsymbol=getSymbol(frameSync);\n\t\t\t// Store this in the rotating symbol buffer\n\t\t\taddToSymbolBuffer(symbol);\n\t\t\t// If needed pass the data to the display bar\n\t\t\tif (enableDisplayBar==true) window.displaySymbol(symbol);\n\t\t\t// Set the dibit state\n\t\t\tdibit=symboltoDibit(symbol);\n\t\t\t// Add the dibit to the circular dibit buffer\n\t\t\taddToDitbitBuf(dibit);\n\t\t    // If we have received 144 dibits then we can check for a valid sync sequence\n\t\t\tif (t>=144) {\n\t\t\t\t// If we don't have frame sync then rotate the symbol buffer\n\t\t\t\t// and also find the new minimum and maximum\n\t\t\t\tif ((frameSync==false)||((frameSync==true)&&(symbolcnt%144==0)))\t{\n\t\t\t\t\t// Get the frames 24 sync symbols\n\t\t\t\t\tsyncHighLowlBuf=getSyncSymbols();\n\t\t\t\t\tlmin=1;\n\t\t\t\t\tlmax=-1;\n\t\t\t\t\tfor (a=0;a<24;a++)\t{\n\t\t\t\t\t\tif (syncHighLowlBuf[a]<lmin) lmin=syncHighLowlBuf[a];\n\t\t\t\t\t\tif (syncHighLowlBuf[a]>lmax) lmax=syncHighLowlBuf[a];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Update the volume bar every 25 frames\n\t\t\t\tif ((t%3600)==0)\t{\n\t\t\t\t\thighVol=lineInThread.returnVolumeAverage();\n\t\t\t\t\twindow.updateVolumeBar(highVol);\n\t\t\t\t}\n\t\t\t\t// Check if a frame has a voice or data sync\n\t\t\t\t// If no frame sync do this at any time but if we do have\n\t\t\t\t// frame sync then only do this every 144 bits\n\t\t\t\tif ((frameSync==false)||((frameSync==true)&&(symbolcnt%144==0)))\t{\n\t\t\t\t\t// Identify the frame sync type which returns\n\t\t\t\t\t// 0 if unknown\n\t\t\t\t\t// 1 if voice\n\t\t\t\t\t// 2 if data\n\t\t\t\t\tsyncType=syncCompare(frameSync);\n\t\t\t\t\t// Embedded signalling frame (BS/MS and Direct)\n\t\t\t\t\tif ((frameSync==true)&&(syncType==0)&&(firstframe==false)&&(embeddedFrameCount<7))\t{\n\t\t\t\t\t\t// Increment the embedded frame counter\n\t\t\t\t\t\tembeddedFrameCount++;\n\t\t\t\t\t\tlastsynctype=13;\n\t\t\t\t\t\treturn (13);\n\t\t\t\t\t}\t\n\t\t\t\t\t// BS Data frame\n\t\t\t\t\tif (syncType==2) {\n\t\t\t\t\t\t// Clear the embedded frame counter\n\t\t\t\t\t\tembeddedFrameCount=0;\n\t\t\t\t\t\tcarrier=true;\n\t\t\t\t\t\tif (frameSync==false)\t{\n\t\t\t\t\t\t\tframeCalcs(lmin,lmax);\n\t\t\t\t\t\t\tframeSync=true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse addToMinMaxBuffer(lmin,lmax);\n\t\t\t\t\t\tif (lastsynctype==-1) firstframe=true;\n\t\t\t\t\t\telse firstframe=false;\n\t\t\t\t\t\tlastsynctype=10;\n\t\t\t\t\t\tmode=0;\n\t\t\t\t\t\treturn (10);\n\t\t\t\t\t}\n\t\t\t\t\t// BS Voice frame\n\t\t\t\t\telse if (syncType==1) {\n\t\t\t\t\t\t// Clear the embedded frame counter\n\t\t\t\t\t\tembeddedFrameCount=0;\n\t\t\t\t\t\tcarrier=true;\n\t\t\t\t\t\tif (frameSync==false)\t{\n\t\t\t\t\t\t\tframeCalcs(lmin,lmax);\n\t\t\t\t\t\t\tframeSync=true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse addToMinMaxBuffer(lmin,lmax);\n\t\t\t\t\t\tif (lastsynctype==-1) firstframe=true;\n\t\t\t\t\t\telse firstframe=false;\n\t\t\t\t\t\tlastsynctype=12;\n\t\t\t\t\t\tmode=0;\n\t\t\t\t\t\treturn (12);\n\t\t\t\t\t}\n\t\t\t\t\t// MS Data frame\n\t\t\t\t\telse if (syncType==4) {\n\t\t\t\t\t\tembeddedFrameCount=0;\n\t\t\t\t\t\tcarrier=true;\n\t\t\t\t\t\tif (frameSync==false)\t{\n\t\t\t\t\t\t\tframeCalcs(lmin,lmax);\n\t\t\t\t\t\t\tframeSync=true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse addToMinMaxBuffer(lmin,lmax);\n\t\t\t\t\t\tif (lastsynctype==-1) firstframe=true;\n\t\t\t\t\t\telse firstframe=false;\n\t\t\t\t\t\tlastsynctype=20;\n\t\t\t\t\t\tmode=1;\n\t\t\t\t\t\treturn (20);\n\t\t\t\t\t}\n\t\t\t\t\t// MS Voice frame\n\t\t\t\t\telse if (syncType==3) {\n\t\t\t\t\t\tembeddedFrameCount=0;\n\t\t\t\t\t\tcarrier=true;\n\t\t\t\t\t\tif (frameSync==false)\t{\n\t\t\t\t\t\t\tframeCalcs(lmin,lmax);\n\t\t\t\t\t\t\tframeSync=true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse addToMinMaxBuffer(lmin,lmax);\n\t\t\t\t\t\tif (lastsynctype==-1) firstframe=true;\n\t\t\t\t\t\telse firstframe=false;\n\t\t\t\t\t\tlastsynctype=22;\n\t\t\t\t\t\tmode=1;\n\t\t\t\t\t\treturn (22);\n\t\t\t\t\t}\n\t\t\t\t\t// RC Sync\n\t\t\t\t\telse if (syncType==5)\t{\n\t\t\t\t\t\tembeddedFrameCount=0;\n\t\t\t\t\t\tcarrier=true;\n\t\t\t\t\t\tif (frameSync==false)\t{\n\t\t\t\t\t\t\tframeCalcs(lmin,lmax);\n\t\t\t\t\t\t\tframeSync=true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse addToMinMaxBuffer(lmin,lmax);\n\t\t\t\t\t\tif (lastsynctype==-1) firstframe=true;\n\t\t\t\t\t\telse firstframe=false;\n\t\t\t\t\t\tlastsynctype=25;\n\t\t\t\t\t\tmode=1;\n\t\t\t\t\t\treturn (25);\n\t\t\t\t\t}\n\t\t\t\t\t// Direct Voice frame 1\n\t\t\t\t\telse if (syncType==6) {\n\t\t\t\t\t\tembeddedFrameCount=0;\n\t\t\t\t\t\tcarrier=true;\n\t\t\t\t\t\tif (frameSync==false)\t{\n\t\t\t\t\t\t\tframeCalcs(lmin,lmax);\n\t\t\t\t\t\t\tframeSync=true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse addToMinMaxBuffer(lmin,lmax);\n\t\t\t\t\t\tif (lastsynctype==-1) firstframe=true;\n\t\t\t\t\t\telse firstframe=false;\n\t\t\t\t\t\tlastsynctype=30;\n\t\t\t\t\t\tmode=2;\n\t\t\t\t\t\treturn (30);\n\t\t\t\t\t}\n\t\t\t\t\t// Direct Data frame 1\n\t\t\t\t\telse if (syncType==7) {\n\t\t\t\t\t\tcarrier=true;\n\t\t\t\t\t\tif (frameSync==false)\t{\n\t\t\t\t\t\t\tframeCalcs(lmin,lmax);\n\t\t\t\t\t\t\tframeSync=true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse addToMinMaxBuffer(lmin,lmax);\n\t\t\t\t\t\tif (lastsynctype==-1) firstframe=true;\n\t\t\t\t\t\telse firstframe=false;\n\t\t\t\t\t\tlastsynctype=31;\n\t\t\t\t\t\tmode=2;\n\t\t\t\t\t\treturn (31);\n\t\t\t\t\t}\n\t\t\t\t\t// Direct Voice frame 2\n\t\t\t\t\telse if (syncType==8) {\n\t\t\t\t\t\tembeddedFrameCount=0;\n\t\t\t\t\t\tcarrier=true;\n\t\t\t\t\t\tif (frameSync==false)\t{\n\t\t\t\t\t\t\tframeCalcs(lmin,lmax);\n\t\t\t\t\t\t\tframeSync=true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse addToMinMaxBuffer(lmin,lmax);\n\t\t\t\t\t\tif (lastsynctype==-1) firstframe=true;\n\t\t\t\t\t\telse firstframe=false;\n\t\t\t\t\t\tlastsynctype=32;\n\t\t\t\t\t\tmode=2;\n\t\t\t\t\t\treturn (32);\n\t\t\t\t\t}\n\t\t\t\t\t// Direct Data frame 2\n\t\t\t\t\telse if (syncType==9) {\n\t\t\t\t\t\tembeddedFrameCount=0;\n\t\t\t\t\t\tcarrier=true;\n\t\t\t\t\t\tif (frameSync==false)\t{\n\t\t\t\t\t\t\tframeCalcs(lmin,lmax);\n\t\t\t\t\t\t\tframeSync=true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse addToMinMaxBuffer(lmin,lmax);\n\t\t\t\t\t\tif (lastsynctype==-1) firstframe=true;\n\t\t\t\t\t\telse firstframe=false;\n\t\t\t\t\t\tlastsynctype=33;\n\t\t\t\t\t\tmode=2;\n\t\t\t\t\t\treturn (33);\n\t\t\t\t\t}\t\t\n\t\t\t\t\t// Rest Data\n\t\t\t\t\telse if (syncType==10) {\n\t\t\t\t\t\tembeddedFrameCount=0;\n\t\t\t\t\t\tcarrier=true;\n\t\t\t\t\t\tif (frameSync==false)\t{\n\t\t\t\t\t\t\tframeCalcs(lmin,lmax);\n\t\t\t\t\t\t\tframeSync=true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse addToMinMaxBuffer(lmin,lmax);\n\t\t\t\t\t\tif (lastsynctype==-1) firstframe=true;\n\t\t\t\t\t\telse firstframe=false;\n\t\t\t\t\t\tlastsynctype=33;\n\t\t\t\t\t\tmode=0;\n\t\t\t\t\t\treturn (40);\n\t\t\t\t\t}\t\t\n\t\t\t\t\t\n\t\t\t\t}\n\t\t}\t\t\t\t\t\n\t\t// We had a signal but appear to have lost it\n\t\tif (carrier==true) {\n\t\t\t// If we have missed 12 frames then something is wrong\n\t\t\tif (synctest_pos>=(144*12)) {\n\t\t\t\t// If in debug mode show that sync has been lost\n\t\t\t\tif (debug==true)\t{\n\t\t\t\t\tStringBuilder l=new StringBuilder(250);\n\t\t\t\t\tl.append(getTimeStamp()+\" Sync Lost\");\n\t\t\t\t\tl.append(\" : centre=\"+Integer.toString(centre));\n\t\t\t\t\tl.append(\" max=\"+Integer.toString(max)+\" min=\"+Integer.toString(min)+\" umid=\"+Integer.toString(umid)+\" lmid=\"+Integer.toString(lmid));\n\t\t\t\t\taddLine(l.toString(),Color.BLACK,plainFont);\n\t\t\t\t\tfileWrite(l.toString());\n\t\t\t\t}\n\t\t\t\tframeSync=false;\n\t\t\t\tnoCarrier();\n\t\t\t\treturn (-1);\n\t\t\t\t}\n\t\t\t}\n\t\t// If the hunt has gone on for a while then reset everything\n\t\tif (t>32000) {\n\t\t\tt=0;\n\t\t\tsynctest_pos=0;\n\t\t\t}\n\t\telse synctest_pos++;\n\t\t}\n\t  }\n\t  \n\t// Add a dibit to the circular dibit buffer\n\tvoid addToDitbitBuf (int dibit)\t{\n\t\tdibitCircularBuffer[dibitCircularBufferCounter]=(byte)dibit;\n\t\tdibitCircularBufferCounter++;\n\t\tif (dibitCircularBufferCounter==144) dibitCircularBufferCounter=0;\n\t}\n\t\n\t// Add a symbol to the circular symbol buffer\n\tvoid addToSymbolBuffer (int symbol)\t{\n\t\tsymbolBuffer[symbolBufferCounter]=symbol;\n\t\tsymbolBufferCounter++;\n\t\tif (symbolBufferCounter==144) symbolBufferCounter=0;\n\t}\n\t\n\t// No carrier or carrier lost so clear the variables\n\tpublic void noCarrier ()\t{\n\t\tjitter=-1;\n\t\tlastsynctype=-1;\n\t\tcarrier=false;\n\t\tmax=MAXSTARTVALUE;\n\t\tmin=MINSTARTVALUE;\n\t\tcentre=0;\n\t\tfirstframe=false;\n\t\terrorFreeFrameCount=0;\n\t\tcontinousBadFrameCount=0;\n\t\tmode=-1;\n\t\t// Update the status bar\n\t\twindow.updateSyncLabel(false);\n\t\twindow.setCh1Label(\"Unused\",labelQuiteColour);\n\t\twindow.setCh2Label(\"Unused\",labelQuiteColour);\n\t\twindow.SetColourCodeLabel(-1,labelQuiteColour);\n\t\twindow.setSystemLabel(\"System : Unknown\",labelQuiteColour);\n\t  \t}\n\t\n\t// Given a symbol return a dibit\n\tint symboltoDibit (int symbol)\t{\n\t\t// With Sync\n\t\tif (frameSync==true)\t{\n\t\t\tif (inverted==false)\t{\n\t\t\t\t// Normal\n\t\t\t\tif (symbol>centre) {\n\t\t\t\t\tif (symbol>umid) return 1;\n\t\t\t\t\telse return 0;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (symbol<lmid) return 3;\n\t\t\t\t\telse return 2;\n\t\t\t\t}\n\t\t\t} else\t{\t\n\t\t\t\t// Inverted\n\t\t\t\tif (symbol>centre) {\n\t\t\t\t\tif (symbol>umid) return 3;\n\t\t\t\t\telse return 2;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (symbol<lmid) return 1;\n\t\t\t\t\telse return 0;\n\t\t\t\t}\n\t\t\t}\n\t\t} else\t{\n\t\t\t\t// No Sync\n\t\t\t\t// Normal\n\t\t\t\tif (inverted==false)\t{\n\t\t\t\t\tif (symbol>0) return 1;\n\t\t\t\t\telse return 3;\n\t\t\t\t}\n\t\t\t\t// Inverted\n\t\t\t\telse\t{\n\t\t\t\t\tif (symbol>0) return 3;\n\t\t\t\t\telse return 1;\n\t\t\t\t}\n\t\t\t}\n\t}\n\t  \t\n\t// Compare the sync sequences held in global arrays with the contents of the dibit circular buffer which returns ..\n\t// 00 unknown\n\t// 01 BS voice\n\t// 02 BS data\n\t// 03 MS voice\n\t// 04 MS data\n\t// 05 RC Sync\n\t// 06 Direct Voice 1\n\t// 07 Direct Data 1\n\t// 08 Direct Voice 2\n\t// 09 Direct Data 2\n\t// 10 Rest Data\n\tprivate int syncCompare(boolean sync)\t{\n\t\tint i,dataSyncBS=0,voiceSyncBS=0,diff,circPos,dataSyncMS=0,voiceSyncMS=0,rcSync=0;\n\t\tint directVoice1=0,directVoice2=0,directData1=0,directData2=0,restData=0;\n\t\t\n\t\t// Allow 5 dibits to be incorrect when syncronised and set the offset\n\t\tif (sync==true)\tdiff=5;\n\t\telse diff=0;\n\t\t\n\t\tcircPos=dibitCircularBufferCounter+66;\n\t\tif (circPos>=144) circPos=circPos-144;\n\t\t\t\n\t\tfor (i=0;i<24;i++)\t{\n\t\t\t// BS\n\t\t\tif (dibitCircularBuffer[circPos]==DMR_VOICE_SYNC_BS[i]) voiceSyncBS++;\n\t\t\tif (dibitCircularBuffer[circPos]==DMR_DATA_SYNC_BS[i]) dataSyncBS++;\n\t\t\t// MS\n\t\t\tif (dibitCircularBuffer[circPos]==DMR_VOICE_SYNC_MS[i]) voiceSyncMS++;\n\t\t\tif (dibitCircularBuffer[circPos]==DMR_DATA_SYNC_MS[i]) dataSyncMS++;\n\t\t\tif (dibitCircularBuffer[circPos]==DMR_RC_SYNC[i]) rcSync++;\n\t\t\t// Direct Slot 1\n\t\t\tif (dibitCircularBuffer[circPos]==DMR_VOICE_SYNC_DIRECT1[i]) directVoice1++;\n\t\t\tif (dibitCircularBuffer[circPos]==DMR_DATA_SYNC_DIRECT1[i]) directData1++;\n\t\t\t// Direct Slot 2\n\t\t\tif (dibitCircularBuffer[circPos]==DMR_VOICE_SYNC_DIRECT2[i]) directVoice2++;\n\t\t\tif (dibitCircularBuffer[circPos]==DMR_DATA_SYNC_DIRECT2[i]) directData2++;\n\t\t\t// Rest Data\n\t\t\tif (dibitCircularBuffer[circPos]==DMR_DATA_REST_SYNC_BS[i]) restData++;\n\t\t\t// Increment the circular buffer counter\n\t\t\tcircPos++;\n\t\t\tif (circPos==144) circPos=0;\n\t\t}\n\t\tif ((DMR_VOICE_SYNC_BS.length-voiceSyncBS)<=diff) return 1;\n\t\telse if ((DMR_DATA_SYNC_BS.length-dataSyncBS)<=diff) return 2;\n\t\telse if ((DMR_VOICE_SYNC_MS.length-voiceSyncMS)<=diff) return 3;\n\t\telse if ((DMR_DATA_SYNC_MS.length-dataSyncMS)<=diff) return 4;\n\t\telse if ((DMR_RC_SYNC.length-rcSync)<=diff) return 5;\n\t\telse if ((DMR_VOICE_SYNC_DIRECT1.length-directVoice1)<=diff) return 6;\n\t\telse if ((DMR_DATA_SYNC_DIRECT1.length-directData1)<=diff) return 7;\n\t\telse if ((DMR_VOICE_SYNC_DIRECT2.length-directVoice2)<=diff) return 8;\n\t\telse if ((DMR_DATA_SYNC_DIRECT2.length-directData2)<=diff) return 9;\n\t\telse if ((DMR_DATA_REST_SYNC_BS.length-restData)<=diff) return 10;\n\t\telse return 0;\t\n\t}\n\t\n\t// Extract just the 24 symbols of the sync sequence and return them in an array\n\tprivate int[] getSyncSymbols()\t{\n\t\tint i,circPos;\n\t\tint syms[]=new int[24];\n\t\tcircPos=symbolBufferCounter+66;\n\t\tif (circPos>=144) circPos=circPos-144;\n\t\tfor (i=0;i<24;i++)\t{\n\t\t\tsyms[i]=symbolBuffer[circPos];\n\t\t\tcircPos++;\n\t\t\tif (circPos==144) circPos=0;\n\t\t}\n\t\treturn syms;\t\n\t}\n\t\n\t// Returns the current sync type\n\tpublic int getSyncType()\t{\n\t\treturn this.synctype;\n\t}\n\t\n\t// Adds a line to the display as long as pause isn't enabled\n\tpublic void addLine(final String line, final Color col, final Font font) {\n\t\tif (pauseScreen==true) return;\n\t\telse display_view.add_line(line,col,font);\n\t}\n\n\t// Return a time stamp\n\tpublic String getTimeStamp() {\n\t\tDate now=new Date();\n\t\tDateFormat df=DateFormat.getTimeInstance();\n\t\treturn df.format(now);\n\t}\t\n\t\n\t// Return a date stamp\n\tpublic String getDateStamp()\t{\n\t\tDate now=new Date();\n\t\tDateFormat df=DateFormat.getDateInstance();\n\t\treturn df.format(now);\n\t}\n\t\n\t// Handle an incoming DMR Frame\n\tvoid processFrame ()\t{\n\t\tif (firstframe==true)\t{\n\t\t\t// Clear the max min buffer counter so we don't use old values\n\t\t\tmaxminBufferCounter=0;\n\t\t\t// If debug enabled record obtaining sync\n\t\t\tif (debug==true)\t{\n\t\t\t\tStringBuilder l=new StringBuilder(250);\n\t\t\t\tif (synctype==12) l.append(getTimeStamp()+\" DMR BS Voice Sync Acquired\");\n\t\t\t\telse if (synctype==10) l.append(getTimeStamp()+\" DMR BS Data Sync Acquired : centre=\"+Integer.toString(centre)+\" max=\"+Integer.toString(max)+\" min=\"+Integer.toString(min)+\" umid=\"+Integer.toString(umid)+\" lmid=\"+Integer.toString(lmid));\n\t\t\t\telse if (synctype==22) l.append(getTimeStamp()+\" DMR MS Voice Sync Acquired : centre=\"+Integer.toString(centre)+\" max=\"+Integer.toString(max)+\" min=\"+Integer.toString(min)+\" umid=\"+Integer.toString(umid)+\" lmid=\"+Integer.toString(lmid));\n\t\t\t\telse if (synctype==20) l.append(getTimeStamp()+\" DMR MS Data Sync Acquired : centre=\"+Integer.toString(centre)+\" max=\"+Integer.toString(max)+\" min=\"+Integer.toString(min)+\" umid=\"+Integer.toString(umid)+\" lmid=\"+Integer.toString(lmid));\n\t\t\t\telse if (synctype==25) l.append(getTimeStamp()+\" DMR RC Sync Acquired : centre=\"+Integer.toString(centre)+\" max=\"+Integer.toString(max)+\" min=\"+Integer.toString(min)+\" umid=\"+Integer.toString(umid)+\" lmid=\"+Integer.toString(lmid));\t\n\t\t\t\telse if (synctype==30) l.append(getTimeStamp()+\" DMR Direct Voice 1 Sync Acquired : centre=\"+Integer.toString(centre)+\" max=\"+Integer.toString(max)+\" min=\"+Integer.toString(min)+\" umid=\"+Integer.toString(umid)+\" lmid=\"+Integer.toString(lmid));\n\t\t\t\telse if (synctype==31) l.append(getTimeStamp()+\" DMR Direct Data Data 1 Sync Acquired : centre=\"+Integer.toString(centre)+\" max=\"+Integer.toString(max)+\" min=\"+Integer.toString(min)+\" umid=\"+Integer.toString(umid)+\" lmid=\"+Integer.toString(lmid));\n\t\t\t\telse if (synctype==32) l.append(getTimeStamp()+\" DMR Direct Voice 2 Sync Acquired : centre=\"+Integer.toString(centre)+\" max=\"+Integer.toString(max)+\" min=\"+Integer.toString(min)+\" umid=\"+Integer.toString(umid)+\" lmid=\"+Integer.toString(lmid));\n\t\t\t\telse if (synctype==33) l.append(getTimeStamp()+\" DMR Direct Data Data 2 Sync Acquired : centre=\"+Integer.toString(centre)+\" max=\"+Integer.toString(max)+\" min=\"+Integer.toString(min)+\" umid=\"+Integer.toString(umid)+\" lmid=\"+Integer.toString(lmid));\n\t\t\t\telse if (synctype==40) l.append(getTimeStamp()+\" DMR BS Data Rest Sync Acquired : centre=\"+Integer.toString(centre)+\" max=\"+Integer.toString(max)+\" min=\"+Integer.toString(min)+\" umid=\"+Integer.toString(umid)+\" lmid=\"+Integer.toString(lmid));\t\t\n\t\t\t\taddLine(l.toString(),Color.BLACK,plainFont);\n\t\t\t\tfileWrite(l.toString());\n\t\t\t\t}\n\t\t\treturn;\n\t    }\n\t    // Update the sync label\n\t    window.updateSyncLabel(frameSync);\n\t    // Deal with the frame\n\t    if ((synctype==12)||(synctype==22)||(synctype==30)||(synctype==32)) processDMRvoice();\n\t    else if ((synctype==10)||(synctype==20)||(synctype==31)||(synctype==33)||(synctype==40)) processDMRdata ();\n\t    else if (synctype==13) processEmbedded ();\n\t}\n\n\t// Handle a DMR Voice Frame\n\tvoid processDMRvoice ()\t{\t\n\t\tDMRVoice DMRvoice=new DMRVoice();\n\t\tFont font[]=new Font[10];\n\t\tColor lcol[]=new Color[10];\n\t\tString line[]=new String[10];\n\t\tline=DMRvoice.decode(theApp,dibitFrame);\n\t\tfont=DMRvoice.getFonts();\n\t\tlcol=DMRvoice.getColours();\n\t\tframeCount++;\n\t\tif (DMRvoice.isError()==false)\t{\n\t\t\tbadFrameCount++;\n\t\t\tcontinousBadFrameCount++;\n\t\t\tline[0]=getTimeStamp()+\" DMR Voice Frame - Error !\";\n\t\t\tlcol[0]=Color.RED;\n\t\t}\n\t\telse\t{\n\t\t\tcontinousBadFrameCount=0;\n\t\t}\n\t\tif (debug==true)\t{\n\t\t\tline[0]=line[0]+dispSymbolsSinceLastFrame();\n\t\t\tlcol[8]=Color.BLACK;\n\t\t\tlcol[9]=Color.BLACK;\n\t\t\tfont[8]=plainFont;\n\t\t\tfont[9]=plainFont;\n\t\t\tline[8]=returnDibitBufferPercentages();\n\t\t\tline[9]=displayDibitBuffer();\n\t\t}\n\t\t// If this frame contains errors and the user wants to display only good ones\n\t\t// then stop them being shown\n\t\tif ((DMRvoice.isError()==false)&&(displayOnlyGoodFrames==true)) DMRvoice.setShouldDisplay(false);\n\t\t// Display the info\n\t\tif ((DMRvoice.getShouldDisplay()==true)&&(displayVoiceFrames==true)) displayLines(line,lcol,font);\n\t}\n\t\n\t// Handle a DMR Data Frame\n\tvoid processDMRdata ()\t{\n\t\tDMRDataDecode DMRdata=new DMRDataDecode();\n\t\tFont font[]=new Font[10];\n\t\tColor lcol[]=new Color[10];\n\t\tString line[]=new String[10];\n\t\tline=DMRdata.decode(theApp,dibitFrame);\n\t\tfont=DMRdata.getFonts();\n\t\tlcol=DMRdata.getColours();\n\t\tframeCount++;\n\t\tif (DMRdata.isError()==false)\t{\n\t\t\tbadFrameCount++;\n\t\t\tline[0]=getTimeStamp()+\" DMR Data Frame - Error !\";\n\t\t\tlcol[0]=Color.RED;\n\t\t\tfont[0]=plainFont;\n\t\t\tline[2]=null;\n\t\t\t// Record that there has been a frame with an error\n\t\t\terrorFreeFrameCount=0;\n\t\t\tcontinousBadFrameCount++;\n\t\t}\n\t\telse\t{\n\t\t\t// Record that there has been an error free frame\n\t\t\terrorFreeFrameCount++;\n\t\t}\n\t\tif (debug==true)\t{\n\t\t\tline[0]=line[0]+dispSymbolsSinceLastFrame();\n\t\t\tlcol[8]=Color.BLACK;\n\t\t\tlcol[9]=Color.BLACK;\n\t\t\tfont[8]=plainFont;\n\t\t\tfont[9]=plainFont;\n\t\t\tline[8]=returnDibitBufferPercentages();\n\t\t\tline[9]=displayDibitBuffer();\n\t\t}\n\t\t// If this frame contains errors and the user wants to display only good ones\n\t\t// then stop them being shown\n\t\tif ((DMRdata.isError()==false)&&(displayOnlyGoodFrames==true)) DMRdata.setShouldDisplay(false);\n\t\t// Display the info\n\t\tif (DMRdata.getShouldDisplay()==true) displayLines(line,lcol,font);\n\t}\n\t\n\t// Handle an embedded frame\n\tvoid processEmbedded ()\t{\n\t\tDMREmbedded DMRembedded=new DMREmbedded();\n\t\tColor lcol[]=new Color[10];\n\t\tFont font[]=new Font[10];\n\t\tString line[]=new String[10];\n\t\tline=DMRembedded.decode(theApp,dibitFrame);\n\t\tfont=DMRembedded.getFonts();\n\t\tlcol=DMRembedded.getColours();\n\t\tframeCount++;\n\t\tif (DMRembedded.isError()==false)\t{\n\t\t\tbadFrameCount++;\n\t\t\tline[0]=getTimeStamp()+\" DMR Embedded Frame - Error !\";\n\t\t\tlcol[0]=Color.RED;\n\t\t\tfont[0]=plainFont;\n\t\t\tline[2]=null;\n\t\t\t// Record that there has been a frame with an error\n\t\t\terrorFreeFrameCount=0;\n\t\t\tcontinousBadFrameCount++;\n\t\t}\n\t\telse\t{\n\t\t\t// Set last sync type to 14 to show this was a good embedded frame\n\t\t\tlastsynctype=14;\n\t\t\tcontinousBadFrameCount=0;\n\t\t}\n\t\tif (debug==true)\t{\n\t\t\tline[0]=line[0]+dispSymbolsSinceLastFrame();\n\t\t\tlcol[8]=Color.BLACK;\n\t\t\tlcol[9]=Color.BLACK;\n\t\t\tfont[8]=plainFont;\n\t\t\tfont[9]=plainFont;\n\t\t\tline[8]=returnDibitBufferPercentages();\n\t\t\tline[9]=displayDibitBuffer();\n\t\t}\n\t\t// If this frame contains errors and the user wants to display only good ones\n\t\t// then stop them being shown\n\t\tif ((DMRembedded.isError()==false)&&(displayOnlyGoodFrames==true)) DMRembedded.setShouldDisplay(false);\n\t\t// Display the info\n\t\tif (DMRembedded.getShouldDisplay()==true) displayLines(line,lcol,font);\n\t}\n\n\t// Display a group of lines\n\tvoid displayLines (String line[],Color col[],Font font[])\t{\n\t\tint a;\n\t\tint len=line.length;\n\t\tfor (a=(len-1);a>=0;a--)\t{\n\t\t\tif (line[a]!=null) addLine(line[a],col[a],font[a]);\n\t\t}\n\t\t// Log to disk if needed\n\t\tif (logging==true)\t{\n\t\t\tfor (a=0;a<len;a++)\t{\n\t\t\t\tif (line[a]!=null) fileWrite(line[a]);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// Write to a string to the logging file\n\tpublic boolean fileWrite(String fline) {\n\t\t// Add a CR to the end of each line\n\t\tfline=fline+\"\\r\\n\";\n\t\t// If we aren't logging don't try to do anything\n\t\tif (logging==false)\n\t\t\treturn false;\n\t\ttry {\n\t\t\tfile.write(fline);\n\t\t\tfile.flush();\n\t\t} catch (Exception e) {\n\t\t\t// Stop logging as we have a problem\n\t\t\tlogging=false;\n\t\t\tSystem.out.println(\"\\nError writing to the logging file\");\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t// Make up a string for the quick log file\n\tpublic void quickLogData(String line,int a,int b,int c,String extra)\t{\n\t\tString tline=getDateStamp()+\",\"+getTimeStamp()+\",\"+Integer.toString(colourCode)+\",\"+line+\",\"+Integer.toString(a)+\",\"+Integer.toString(b)+\",\"+Integer.toString(c)+\",\"+extra;\n\t\tquickLogWrite(tline);\n\t}\n\t\n\t// Write to a string to the logging file\n\tprivate boolean quickLogWrite(String fline) {\n\t\t// Add a CR to the end of each line\n\t\tfline=fline+\"\\r\\n\";\n\t\t// If we aren't logging don't try to do anything\n\t\tif (quickLog==false)\n\t\t\treturn false;\n\t\ttry {\n\t\t\tquickLogFile.write(fline);\n\t\t\tquickLogFile.flush();\n\t\t} catch (Exception e) {\n\t\t\t// Stop logging as we have a problem\n\t\t\tquickLog=false;\n\t\t\tSystem.out.println(\"\\nError writing to the quick log file\");\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\t\n\t// Display the number of symbols since the last frame with a valid sync\n\tpublic String dispSymbolsSinceLastFrame ()\t{\n\t\t// Don't display anything if 144 symbols since the last frame.\n\t\tif (symbolcnt!=144)\t{\n\t\t\tString l=\" (Symbols=\"+Integer.toString(symbolcnt)+\")\";\n\t\t\treturn l;\n\t\t}\n\t\telse return \"\";\n\t}\n\t\n\t// Grab a sample and write it to the capture file\n\tpublic void audioDump (int sample)\t{\n\t\ttry\t{\n\t\t\tcaptureFile.write(\"\\r\\n\");\t\n\t\t\tcaptureFile.write(Integer.toString(sample));\n\t\t\t}\n\t\tcatch (Exception e)\t{\n\t\t\tSystem.err.println(\"Error: \" + e.getMessage());\n\t\t\tcaptureMode=false;\n\t\t}\n\t\tcaptureCount++;\n\t\tif (captureCount>48000)\t{\n\t\t\tcloseCaptureFile();\n\t\t\tcaptureMode=false;\n\t\t}\n\t\t}\n\t\n\t// Write a line to the debug file\n\tpublic void debugDump (String line)\t{\n\t    try\t{\n\t    \tFileWriter dfile=new FileWriter(\"debug.csv\",true);\n\t    \tdfile.write(line);\n\t    \tdfile.write(\"\\r\\n\");\n\t    \tdfile.flush();  \n\t    \tdfile.close();\n\t    \t}catch (Exception e)\t{\n\t    \t\tSystem.err.println(\"Error: \" + e.getMessage());\n\t    \t\t}\n\t\t}\n\t\t\n\t// Display the dibit buffer as a string\n\tpublic String displayDibitBuffer ()\t{\n\t\tStringBuilder lb=new StringBuilder(500);\n\t\tint a;\n\t\tfor (a=0;a<144;a++)\t{\n\t\t\tlb.append(Integer.toString(dibitFrame[a]));\n\t\t}\n\t\treturn lb.toString();\n\t}\n\t\n\t// Return a string showing the percentages of each dibit in the dibit buffer\n\tpublic String returnDibitBufferPercentages ()\t{\n\t\tStringBuilder dline=new StringBuilder(500);\n\t\tint a,c0=0,c1=0,c2=0,c3=0;\n\t\tfloat mp=(float) (144-24.0);\n\t\tfor (a=0;a<144;a++)\t{\n\t\t\t// Exclude the sync burst from the percentages \n\t\t\tif ((a<66)||(a>89))\t{\n\t\t\tif (dibitFrame[a]==0) c0++;\n\t\t\tif (dibitFrame[a]==1) c1++;\n\t\t\tif (dibitFrame[a]==2) c2++;\n\t\t\tif (dibitFrame[a]==3) c3++;\n\t\t\t}\n\t\t}\n\t\tc0=(int)(((float)c0/mp)*(float)100);\n\t\tc1=(int)(((float)c1/mp)*(float)100);\n\t\tc2=(int)(((float)c2/mp)*(float)100);\n\t\tc3=(int)(((float)c3/mp)*(float)100);\n\t\t// Write this to a line\n\t\tdline.append(\"Dibit 0=\"+Integer.toString(c0)+\"% \");\t\n\t\tdline.append(\"Dibit 1=\"+Integer.toString(c1)+\"% \");\t\n\t\tdline.append(\"Dibit 2=\"+Integer.toString(c2)+\"% \");\t\n\t\tdline.append(\"Dibit 3=\"+Integer.toString(c3)+\"% \");\t\n\t\treturn dline.toString();\n\t}\n\t\t\n\tpublic boolean isDebug() {\n\t\treturn debug;\n\t}\n\n\tpublic void setDebug(boolean debug) {\n\t\tthis.debug=debug;\n\t}\n\n\t// Put the dibits into dibitFrame in the correct order from the circular dibit buffer\n\tprivate void createDibitFrame()\t{\n\t\tint i,circPos;\n\t\tcircPos=dibitCircularBufferCounter-144;\n\t\tif (circPos<0) circPos=144+circPos;\n\t\tfor (i=0;i<144;i++)\t{\n\t\t\tdibitFrame[i]=dibitCircularBuffer[circPos];\n\t\t\tcircPos++;\n\t\t\tif (circPos==144) circPos=0;\n\t\t}\n\t}\n\t\n\t// Set the audio capture mode\n\tpublic void setCapture (boolean c)\t{\n\t\tif ((captureMode==false)&&(c==true))\t{\n\t\t\topenCaptureFile();\n\t\t\tcaptureCount=0;\n\t\t}\n\t\telse if ((captureMode==true)&&(c==false))\t{\n\t\t\tcloseCaptureFile();\n\t\t\tcaptureMode=false;\n\t\t}\n\t}\n\t\n\t// Tell the program if it is in audio capture mode\n\tpublic boolean isCapture (){\n\t\treturn captureMode;\n\t}\n\t\n\t// Open the capture file\n\tprivate void openCaptureFile()\t{\n\t\ttry\t{\n\t\t\tcaptureFile=new FileWriter(\"capture_dump.csv\");\n\t\t\tcaptureMode=true;\n\t\t}\n\t\tcatch (Exception e)\t{\n\t\t\tcaptureMode=false;\n\t\t}\n\t}\n\t\n\t// Close the capture file\n\tprivate void closeCaptureFile()\t{\n\t\ttry\t{\n\t\t\tcaptureFile.flush();\n\t\t\tcaptureFile.close();\n\t\t}\n\t\tcatch (Exception e)\t{\n\t\t\tJOptionPane.showMessageDialog(null,\"Error closing the capture file\",\"DMRDecode\", JOptionPane.INFORMATION_MESSAGE);\n\t\t}\n\t\t// We aren't in capture mode any longer\n\t\tcaptureMode=false;\n\t}\n\n\t\n\t// Enable or disable the symbol display bar\n\tpublic void setEnableDisplayBar(boolean enableDisplayBar) {\n\t\tthis.enableDisplayBar=enableDisplayBar;\n\t\twindow.switchDisplayBar(this.enableDisplayBar);\n\t}\n\n\t// Tell other classes if the symbol display bar is enabled or disabled \n\tpublic boolean isEnableDisplayBar() {\n\t\treturn enableDisplayBar;\n\t}\n\t\n\t// Add a sample to the samples ahead buffer\n\tprivate void addToSamplesAheadBuffer (int sam)\t{\n\t\tsamplesAheadBuffer[samplesAheadCounter]=sam;\n\t\tsamplesAheadCounter++;\n\t\tif (samplesAheadCounter==SAMPLESAHEADSIZE) samplesAheadCounter=0;\n\t}\n\t\n\t// Get the oldest sample from the samples ahead buffer\n\tprivate int getOldestSample()\t{\n\t\treturn samplesAheadBuffer[samplesAheadCounter];\n\t}\n\t\n\t// Get a sample either from the sound card \n\tprivate int getSample (boolean jitmode)\t{\n\t\tint sample=0;\n\t\t// Get the sample from the sound card via the sound thread\n\t\ttry\t{\n\t\t\tsample=inPipeData.readInt();\n\t\t\t}\n\t\tcatch (Exception e)\t{\n\t\t\tJOptionPane.showMessageDialog(null,\"Error in getSample()\",\"DMRDecode\", JOptionPane.INFORMATION_MESSAGE);\n\t\t\t}\n\t\t// If in capture mode record the sample in the capture file\n\t\t// but don't do this in jitter adjust mode\n\t\tif ((captureMode==true)&&(jitmode==false)) audioDump(sample);\n\t\t// Add this to the circular samples ahead buffer\n\t\taddToSamplesAheadBuffer(sample);\n\t\t// Pull the oldest sample from the circular samples ahead buffer\n\t\treturn getOldestSample();\n\t}\n\t\n\t// The the max and min values to a circular buffer of values\n\tprivate void addToMinMaxBuffer (int tmin,int tmax)\t{\n\t\tmaxBuffer[maxminBufferCounter]=tmax;\n\t\tminBuffer[maxminBufferCounter]=tmin;\n\t\tmaxminBufferCounter++;\n\t\t// When the buffer reaches its maximum size use calculate new parameters\n\t\tif (maxminBufferCounter==MAXMINBUFSIZE)\t{\n\t\t\tmaxminBufferCounter=0;\n\t\t\tcalcAverageMinMax();\n\t\t}\n\t}\n\t\n\t// Calculate new min and max parameters as averages from the circular buffer\n\tprivate void calcAverageMinMax()\t{\n\t\tint a,totalmax=0,totalmin=0;\n\t\tfor (a=0;a<MAXMINBUFSIZE;a++)\t{\n\t\t\ttotalmax=totalmax+maxBuffer[a];\n\t\t\ttotalmin=totalmin+minBuffer[a];\n\t\t}\n\t\ttotalmax=totalmax/MAXMINBUFSIZE;\n\t\ttotalmin=totalmin/MAXMINBUFSIZE;\n\t\tjitterCounter=0;\n\t\tframeCalcs(totalmin,totalmax);\n\t}\n\n\tpublic void setDisplayCACH(boolean displayCACH) {\n\t\tthis.displayCACH = displayCACH;\n\t}\n\n\tpublic boolean isDisplayCACH() {\n\t\treturn displayCACH;\n\t}\n\n\tpublic void setDisplayIdlePDU(boolean displayIdlePDU) {\n\t\tthis.displayIdlePDU = displayIdlePDU;\n\t}\n\n\tpublic boolean isDisplayIdlePDU() {\n\t\treturn displayIdlePDU;\n\t}\n\n\tpublic void setDisplayOnlyGoodFrames(boolean displayOnlyGoodFrames) {\n\t\tthis.displayOnlyGoodFrames = displayOnlyGoodFrames;\n\t}\n\n\tpublic boolean isDisplayOnlyGoodFrames() {\n\t\treturn displayOnlyGoodFrames;\n\t}\n\t\n\tpublic void setCh1Label (String label,Color col)\t{\n\t\twindow.setCh1Label(label,col);\n\t}\n\t\n\tpublic void setCh2Label (String label,Color col)\t{\n\t\twindow.setCh2Label(label,col);\n\t}\n\t\n\tpublic boolean getLogging()\t{\n\t\treturn logging;\n\t}\n\t\n\tpublic void setLogging (boolean log)\t{\n\t\tlogging=log;\n\t}\n\n\tpublic void setPauseScreen(boolean pauseScreen) {\n\t\tthis.pauseScreen=pauseScreen;\n\t}\n\n\tpublic boolean isPauseScreen() {\n\t\treturn pauseScreen;\n\t}\n\n\tpublic void setQuickLog(boolean quickLog) {\n\t\tthis.quickLog = quickLog;\n\t}\n\n\tpublic boolean isQuickLog() {\n\t\treturn quickLog;\n\t}\n\n\tpublic void setColourCode(int cc) {\n\t\tthis.colourCode=cc;\n\t\twindow.SetColourCodeLabel(cc,labelBusyColour);\n\t}\n\t\n\tpublic void setSystemLabel(String txt)\t{\n\t\twindow.setSystemLabel(txt,labelBusyColour);\n\t}\n\n\tpublic int getColourCode() {\n\t\treturn colourCode;\n\t}\n\t\n\tpublic void clearScreen()\t{\n\t\tdisplay_view.clearScreen();\n\t}\n\t\n\t// Gets all the text on the screen and returns it as a string\n\tpublic String getAllText()\t{\n\t\treturn\tdisplay_view.getText();\n\t}\n\t\n\t// Save the current settings as DMRDecode_settings.xml\n\tpublic boolean saveCurrentSettings ()\t{\n\t\tFileWriter xmlfile;\n\t\tString line;\n\t\t// Open the default file settings //\n\t\ttry {\n\t\t\txmlfile=new FileWriter(\"DMRDecode_settings.xml\");\n\t\t\t// Start the XML file //\n\t\t\tline=\"<?xml version='1.0' encoding='utf-8' standalone='yes'?><settings>\";\n\t\t\txmlfile.write(line);\n\t\t\t// Debug mode\n\t\t\tline=\"<debug val='\";\n\t\t\tif (debug==true) line=line+\"TRUE\";\n\t\t\telse line=line+\"FALSE\";\n\t\t\tline=line+\"'/>\";\n\t\t\txmlfile.write(line);\n\t\t\t// Invert\n\t\t\tline=\"<invert val='\";\n\t\t\tif (inverted==true) line=line+\"TRUE\";\n\t\t\telse line=line+\"FALSE\";\n\t\t\tline=line+\"'/>\";\n\t\t\txmlfile.write(line);\t\t\n\t\t\t// Enable Symbol Display\n\t\t\tline=\"<symbolDisplay val='\";\n\t\t\tif (enableDisplayBar==true) line=line+\"TRUE\";\n\t\t\telse line=line+\"FALSE\";\n\t\t\tline=line+\"'/>\";\n\t\t\txmlfile.write(line);\t\t\t\n\t\t\t// Display CACH\n\t\t\tline=\"<displayCACH val='\";\n\t\t\tif (displayCACH==true) line=line+\"TRUE\";\n\t\t\telse line=line+\"FALSE\";\n\t\t\tline=line+\"'/>\";\n\t\t\txmlfile.write(line);\n\t\t\t// Only display good frames\n\t\t\tline=\"<goodFramesOnly val='\";\n\t\t\tif (displayOnlyGoodFrames==true) line=line+\"TRUE\";\n\t\t\telse line=line+\"FALSE\";\n\t\t\tline=line+\"'/>\";\n\t\t\txmlfile.write(line);\n\t\t\t// Display IDLE PDUs\n\t\t\tline=\"<idlePDU val='\";\n\t\t\tif (displayIdlePDU==true) line=line+\"TRUE\";\n\t\t\telse line=line+\"FALSE\";\n\t\t\tline=line+\"'/>\";\n\t\t\txmlfile.write(line);\t\n\t\t\t// Display Voice Frames\n\t\t\tline=\"<voiceFrames val='\";\n\t\t\tif (displayVoiceFrames==true) line=line+\"TRUE\";\n\t\t\telse line=line+\"FALSE\";\n\t\t\tline=line+\"'/>\";\n\t\t\txmlfile.write(line);\t\n\t\t\t// Save the current audio source\n\t\t\tline=\"<audioDevice val='\"+lineInThread.getMixerName()+\"'/>\";\n\t\t\txmlfile.write(line);\n\t\t\t// All done so close the root item //\n\t\t\tline=\"</settings>\";\n\t\t\txmlfile.write(line);\n\t\t\t// Flush and close the file //\n\t\t\txmlfile.flush();\n\t\t\txmlfile.close();\n\t\t\t} catch (Exception e) {\n\t\t\t\tJOptionPane.showMessageDialog(null,\"Error : Unable to create the file DMRDecode_settings.xml\\n\"+e.toString(),\"Rivet\", JOptionPane.ERROR_MESSAGE);\n\t\t\t\treturn false;\n\t\t\t}\t\n\t\treturn true;\n\t}\n\t\n\t// Read in the DMRDecode_settings.xml file //\n\tpublic void readDefaultSettings() throws SAXException, IOException,ParserConfigurationException {\n\t\t\t// Create a parser factory and use it to create a parser\n\t\t\tSAXParserFactory parserFactory=SAXParserFactory.newInstance();\n\t\t\tSAXParser parser=parserFactory.newSAXParser();\n\t\t\t// This is the name of the file you're parsing\n\t\t\tString filename=\"DMRDecode_settings.xml\";\n\t\t\t// Instantiate a DefaultHandler subclass to handle events\n\t\t\tsaxHandler handler=new saxHandler();\n\t\t\t// Start the parser. It reads the file and calls methods of the handler.\n\t\t\tparser.parse(new File(filename),handler);\n\t\t}\n\t\n\tpublic boolean isDisplayVoiceFrames() {\n\t\treturn displayVoiceFrames;\n\t}\n\n\tpublic void setDisplayVoiceFrames(boolean displayVoiceFrames) {\n\t\tthis.displayVoiceFrames = displayVoiceFrames;\n\t}\n\t\n\t// Change the audio mixer\n\tpublic boolean changeMixer(String mixerName)\t{\n\t\t// Tell the audio in thread to change its mixer\n\t\treturn lineInThread.changeMixer(mixerName);\n\t}\n\n\t// This class handles the SAX events\n\tpublic class saxHandler extends DefaultHandler {\n\t\t\tString value;\n\t\t\t\n\t\t\tpublic void endElement(String namespaceURI,String localName,String qName) throws SAXException {\t\n\t\t\t}\n\n\t\t\tpublic void characters(char[] ch,int start,int length) throws SAXException {\n\t\t\t\t// Extract the element value as a string //\n\t\t\t\tString tval=new String(ch);\n\t\t\t\tvalue=tval.substring(start,(start+length));\n\t\t\t}\n\t\t\t\n\t\t\t// Handle an XML start element //\n\t\t\tpublic void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {\n\t\t\t\t// Check an element has a value //\n\t\t\t\tif (attributes.getLength()>0) {\n\t\t\t\t\t// Get the elements value //\n\t\t\t\t\tString aval=attributes.getValue(0);\n\t\t\t\t\t// Debug mode //\n\t\t\t\t\tif (qName.equals(\"debug\")) {\n\t\t\t\t\t\tif (aval.equals(\"TRUE\")) setDebug(true);\n\t\t\t\t\t\telse setDebug(false);\t\n\t\t\t\t\t}\n\t\t\t\t\t// Invert\n\t\t\t\t\tif (qName.equals(\"invert\")) {\n\t\t\t\t\t\tif (aval.equals(\"TRUE\")) inverted=true;\n\t\t\t\t\t\telse inverted=false;\t\n\t\t\t\t\t}\t\t\t\n\t\t\t\t\t// Symbol display\n\t\t\t\t\tif (qName.equals(\"symbolDisplay\")) {\n\t\t\t\t\t\tif (aval.equals(\"TRUE\")) enableDisplayBar=true;\n\t\t\t\t\t\telse enableDisplayBar=false;\t\n\t\t\t\t\t}\n\t\t\t\t\t// Display CACH\n\t\t\t\t\tif (qName.equals(\"displayCACH\")) {\n\t\t\t\t\t\tif (aval.equals(\"TRUE\")) displayCACH=true;\n\t\t\t\t\t\telse displayCACH=false;\t\n\t\t\t\t\t}\t\t\t\t\t\n\t\t\t\t\t// Display only good frames\n\t\t\t\t\tif (qName.equals(\"goodFramesOnly\")) {\n\t\t\t\t\t\tif (aval.equals(\"TRUE\")) displayOnlyGoodFrames=true;\n\t\t\t\t\t\telse displayOnlyGoodFrames=false;\t\n\t\t\t\t\t}\t\t\t\t\t\n\t\t\t\t\t// Display Idle PDUs\n\t\t\t\t\tif (qName.equals(\"idlePDU\")) {\n\t\t\t\t\t\tif (aval.equals(\"TRUE\")) displayIdlePDU=true;\n\t\t\t\t\t\telse displayIdlePDU=false;\t\n\t\t\t\t\t}\t\n\t\t\t\t\t// Display Voice Frames\n\t\t\t\t\tif (qName.equals(\"voiceFrames\")) {\n\t\t\t\t\t\tif (aval.equals(\"TRUE\")) displayVoiceFrames=true;\n\t\t\t\t\t\telse displayVoiceFrames=false;\t\n\t\t\t\t\t}\t\n\t\t\t\t\t// The audio input source\n\t\t\t\t\tif (qName.equals(\"audioDevice\"))\t{\n\t\t\t\t\t\tif (lineInThread.changeMixer(aval)==false) {\n\t\t\t\t\t\t\tJOptionPane.showMessageDialog(null,\"Error changing mixer\\n\"+lineInThread.getMixerErrorMessage(),\"DMRDecode\",JOptionPane.ERROR_MESSAGE);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\n\t\t\t\t}\t\n\t\t\t\t\n\t\t\t}\n\t\t}\n\t\n\t// Data types are as follows ..\n\t// 01 - Unconfirmed\n\t// 02 - Confirmed\n\t// 03 - Response\n\t// 04 - Proprietary\n\t// 05 - Status/Precoded\n\t// 06 - Raw Short\n\t// 07 - Defined Short\n\t// 08 - Unified Data Transport\n\t\n\t// Set the current channels incoming data type\n\t// also clear the current channels data block counter\n\tpublic void setCurrentIncomingDataType (int type)\t{\n\t\tif (currentChannel==1)\t{\n\t\t\tincomingDataType[0]=type;\n\t\t\tdataBlocksReceived[0]=0;\n\t\t}\n\t\telse\t{\n\t\t\tincomingDataType[1]=type;\n\t\t\tdataBlocksReceived[1]=0;\n\t\t}\n\t}\n\t\n\t// Get the current channels incoming data type\n\tpublic int getCurrentIncomingDataType ()\t{\n\t\tif (currentChannel==1) return incomingDataType[0];\n\t\telse return incomingDataType[1];\n\t}\n\t\n\t// Set the current channels data blocks to follow\n\tpublic void setCurrentDataBlocksToFollow (int blocks)\t{\n\t\tif (currentChannel==1) dataBlocksToFollow[0]=blocks;\n\t\telse dataBlocksToFollow[1]=blocks;\n\t}\n\t\n\t// Get the current channels data blocks to follow\n\tpublic int getCurrentDataBlocksToFollow ()\t{\n\t\tif (currentChannel==1) return dataBlocksToFollow[0];\n\t\telse return dataBlocksToFollow[1];\n\t}\t\n\t\n\t// Get the current channels received data blocks counter\n\tpublic int getCurrentDataBlocksReceived ()\t{\n\t\tif (currentChannel==1) return dataBlocksReceived[0];\n\t\telse return dataBlocksReceived[1];\n\t}\t\n\t\n\t// Increment the current channels data block counter\n\tpublic void incrementCurrentDataBlocksReceived ()\t{\n\t\tif (currentChannel==1) dataBlocksReceived[0]++;\n\t\telse dataBlocksReceived[1]++;\n\t}\n\t\n\t// A getter for the mode\n\tpublic int getMode ()\t{\n\t\treturn this.mode;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/DMREmbedded.java",
    "content": "package com.dmr;\n\nimport java.awt.Color;\nimport java.awt.Font;\n\npublic class DMREmbedded {\n\tprivate int residueValue;\n\tprivate String line[]=new String[10];\n\tprivate Font fonts[]=new Font[10];\n\tprivate Color colours[]=new Color[10];\n\tprivate boolean resCACH,resEMB;\n\tprivate boolean shouldDisplay=true;\n\tprivate DMRDecode theApp;\n\t\n\tpublic String[] decode (DMRDecode TtheApp,byte[] dibit_buf)\t{\n\t\tString cline;\n\t\ttheApp=TtheApp;\n\t\tint mode=theApp.getMode();\n\t\t// BS only\n\t\tif (mode==0)\t{\n\t\t\tDecodeCACH cachdecode=new DecodeCACH();\n\t\t\t// CACH decode\n\t\t\tcline=cachdecode.decode(theApp,dibit_buf);\n\t\t\tresCACH=cachdecode.isPassErrorCheck();\n\t\t\tif (resCACH==true) {\n\t\t\t\tline[1]=cline;\n\t\t\t\tfonts[1]=theApp.italicFont;\n\t\t\t\tcolours[1]=Color.BLACK;\n\t\t\t\tresEMB=EMBdecode(dibit_buf);\n\t\t\t\t// If short LC data is available then display it\n\t\t\t\tif (cachdecode.getShortLC()==true)\t{\n\t\t\t\t\tline[7]=cachdecode.getShortLCline();\n\t\t\t\t\tfonts[7]=theApp.boldFont;\n\t\t\t\t\tif (cachdecode.getshortLCError()==true)\t{\n\t\t\t\t\t\tcolours[7]=Color.RED;\n\t\t\t\t\t\tif (theApp.isDisplayOnlyGoodFrames()==true) line[7]=null;\n\t\t\t\t\t}\n\t\t\t\t\telse colours[7]=Color.BLACK;\n\t\t\t\t\tcachdecode.clearShortLC();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((resCACH==false)&&(resEMB==false)) theApp.embeddedFrameCount=8;\n\t\t}\n\t\telse\t{\n\t\t\t// MS and Direct\n\t\t\tresEMB=EMBdecode(dibit_buf);\n\t\t}\n\t\t\n\t\t\n\t\ttheApp.frameCount++;\n\t\treturn line;\n\t}\n\n\t// Has there been an error\n\tpublic boolean isError() {\n\t\tif ((resCACH==false)||(resEMB==false)) return false;\n\t\telse return true;\n\t}\n\t\n\t// Error check and decode the EMB\n\tprivate boolean EMBdecode(byte[] dibit_buf)\t{\n\t\tint a,r,cc,lcss;\n\t\tboolean pi;\n\t\tboolean EMDdata[]=new boolean[16];\n\t\t// Convert from dibits into boolean\n\t\t// The EMB is broken into 2 parts either side of the embedded\n\t\t// these need reuniting into a single 20 bit boolean array\n\t\tr=0;\n\t\tfor (a=66;a<70;a++)\t{\n\t\t\tif (dibit_buf[a]==0)\t{\n\t\t\t\tEMDdata[r]=false;\n\t\t\t\tEMDdata[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==1)\t{\n\t\t\t\tEMDdata[r]=false;\n\t\t\t\tEMDdata[r+1]=true;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==2)\t{\n\t\t\t\tEMDdata[r]=true;\n\t\t\t\tEMDdata[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==3)\t{\n\t\t\t\tEMDdata[r]=true;\n\t\t\t\tEMDdata[r+1]=true;\n\t\t\t}\n\t\t\tr=r+2;\n\t\t}\n\t\tfor (a=86;a<90;a++)\t{\n\t\t\tif (dibit_buf[a]==0)\t{\n\t\t\t\tEMDdata[r]=false;\n\t\t\t\tEMDdata[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==1)\t{\n\t\t\t\tEMDdata[r]=false;\n\t\t\t\tEMDdata[r+1]=true;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==2)\t{\n\t\t\t\tEMDdata[r]=true;\n\t\t\t\tEMDdata[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==3)\t{\n\t\t\t\tEMDdata[r]=true;\n\t\t\t\tEMDdata[r+1]=true;\n\t\t\t}\n\t\t\tr=r+2;\n\t\t}\n\t\t// Error check the EMB\n\t\t// If it passes this is a Voice Burst with Embedded Signalling\n\t\tif (QuadResidue1676(EMDdata)==true)\t{\n\t\t\tStringBuilder sb=new StringBuilder(250);\n\t\t\tline[0]=theApp.getTimeStamp()+\" DMR Voice Frame with Embedded Signalling\";\n\t\t\tcolours[0]=Color.BLACK;\n\t\t\tfonts[0]=theApp.boldFont;\n\t\t\t// Colour code\n\t\t\tif (EMDdata[0]==true) cc=8;\n\t\t\telse cc=0;\n\t\t\tif (EMDdata[1]==true) cc=cc+4;\n\t\t\tif (EMDdata[2]==true) cc=cc+2;\n\t\t\tif (EMDdata[3]==true) cc=cc+1;\n\t\t\t// Update the colour code display\n\t\t\tif (theApp!=null) theApp.setColourCode(cc);\n\t\t\t// PI\n\t\t\tpi=EMDdata[4];\n\t\t\t// LCSS\n\t\t\tif (EMDdata[5]==true) lcss=2;\n\t\t\telse lcss=0;\n\t\t\tif (EMDdata[6]==true) lcss++;\n\t\t\t// Display the colour code\n\t\t\tsb.append(\"EMB : Colour Code \"+Integer.toString(cc));\n\t\t\t// PI\n\t\t\tif (pi==true) sb.append(\" : PI=1\");\n\t\t\t// LCSS\n\t\t\tif (lcss==0) sb.append(\" : Single fragment LC \");\n\t\t\telse if (lcss==1) sb.append(\" : First fragment of LC \");\n\t\t\telse if (lcss==2) sb.append(\" : Last fragment of LC\");\n\t\t\telse if (lcss==3) sb.append(\" : Continuation fragment of LC\");\n\t\t\tline[2]=sb.toString();\n\t\t\t// Add this to the embedded data class\n\t\t\ttheApp.embedded_lc.addData(dibit_buf,lcss);\n\t\t\t// Is embedded data ready\n\t\t\tif (theApp.embedded_lc.getDataReady()==true)\t{\n\t\t\t\tString elines[]=theApp.embedded_lc.getLines();\n\t\t\t\t// Display the embedded LC along with a timestamp\n\t\t\t\tline[3]=theApp.getTimeStamp()+\" \"+elines[0];\n\t\t\t}\n\t\t\t// Pass on voice data\n\t\t\tVoiceData voicedata=new VoiceData();\n\t\t\tvoicedata.handleVoice(theApp,dibit_buf);\n\t\t\t// If the user doesn't want to see voice frames set shouldDisplay to false\n\t\t\tif (theApp.isDisplayVoiceFrames()==false) shouldDisplay=false;\n\t\t\t// Return all done\n\t\t\treturn true;\n\t\t}\n\t\telse\t{\n\t\t\t// Is this a Data Frame with Embedded signalling\n\t\t\t// See if its has a slot type field that passes its error check\n\t\t\tSlotType slottype=new SlotType();\n\t\t\tboolean SLOT_TYPEres,BPTCres=false;\n\t\t\tline[0]=theApp.getTimeStamp()+\" DMR Data Frame with Embedded Signalling\";\n\t\t\tcolours[0]=Color.BLACK;\n\t\t\tfonts[0]=theApp.boldFont;\n\t\t\tline[2]=slottype.decode(theApp,dibit_buf);\n\t\t\tSLOT_TYPEres=slottype.isPassErrorCheck();\n\t\t\t// If the slot type is OK try to decode the rest\n\t\t\tif (SLOT_TYPEres==true)\t{\n\t\t\t\tint dataType=slottype.returnDataType();\n\t\t\t\t// PI Header\n\t\t\t\tif (dataType==0)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\t// Display the PI header bits as raw binary\n\t\t\t\t\t\tStringBuilder sb=new StringBuilder();\n\t\t\t\t\t\tint ai;\n\t\t\t\t\t\tfor (ai=0;ai<bits.length;ai++)\t{\n\t\t\t\t\t\t\tif (bits[ai]==true) sb.append(\"1\");\n\t\t\t\t\t\t\telse sb.append(\"0\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tline[3]=sb.toString();\n\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t}\n\t\t\t\t}\t\t\t\t\n\t\t\t\t// Voice LC Header\n\t\t\t\telse if (dataType==1)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\t// TODO : Ensure the Voice LC Headers in Embedded Data Frames pass the Reed Solomon (12,9) error check \n\t\t\t\t\t\tFullLinkControl flc=new FullLinkControl();\n\t\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\t\tclines=flc.decode(theApp,bits);\n\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\tline[5]=clines[2];\n\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Terminator with LC\n\t\t\t\telse if (dataType==2)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\t// TODO : Ensure the Terminator LCs in Embedded Data Frames pass the Reed Solomon (12,9) error check \n\t\t\t\t\t\tFullLinkControl flc=new FullLinkControl();\n\t\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\t\tclines=flc.decode(theApp,bits);\n\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\tline[5]=clines[2];\n\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t\t}\n\t\t\t\t}\t\t\n\t\t\t\t// CSBK\n\t\t\t\telse if (dataType==3)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tcrc tCRC=new crc();\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\t// Does the CSBK pass its CRC test ?\n\t\t\t\t\t\tif (tCRC.crcCSBK(bits)==true)\t{\n\t\t\t\t\t\t\tCSBK csbk=new CSBK();\n\t\t\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\t\tclines=csbk.decode(theApp,bits);\n\t\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\t\tline[5]=clines[2];\t\n\t\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Data Header\n\t\t\t\telse if (dataType==6)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tcrc tCRC=new crc();\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\t// Does the Data Header pass its CRC test ?\n\t\t\t\t\t\tif (tCRC.crcDataHeader(bits)==true)\t{\n\t\t\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\t\tDMRData data=new DMRData(theApp);\n\t\t\t\t\t\t\tclines=data.decodeHeader(bits);\n\t\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\t\tline[5]=clines[2];\n\t\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t\t}\t\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Rate  Data Continuation\n\t\t\t\telse if (dataType==7) {\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tif (bptc19696.decode(dibit_buf)==true)\t{\n\t\t\t\t\t\tboolean bits[]=bptc19696.dataOut();\n\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\t\tDMRData data=new DMRData(theApp);\n\t\t\t\t\t\tclines=data.decodeHalfRate(bits);\n\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\tline[5]=clines[2];\n\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Rate  Data Continuation\n\t\t\t\telse if (dataType==8)\t{\n\t\t\t\t\t// Just use the BPTC19696 class to extract the raw binary from the dibit buffer\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tboolean bits[]=bptc19696.rawOut(dibit_buf);\n\t\t\t\t\tString clines[]=new String[3];\n\t\t\t\t\tDMRData data=new DMRData(theApp);\n\t\t\t\t\tclines=data.decodeThreeQuarterRate(bits);\n\t\t\t\t\t// If clines is null then we have an error\n\t\t\t\t\tif (clines==null)\t{\n\t\t\t\t\t\tBPTCres=false;\n\t\t\t\t\t}\n\t\t\t\t\telse\t{\n\t\t\t\t\t\tBPTCres=true;\n\t\t\t\t\t\tline[3]=clines[0];\n\t\t\t\t\t\tline[4]=clines[1];\n\t\t\t\t\t\tline[5]=clines[2];\n\t\t\t\t\t\tfonts[3]=theApp.boldFont;\n\t\t\t\t\t\tcolours[3]=Color.BLACK;\n\t\t\t\t\t\tfonts[4]=theApp.boldFont;\n\t\t\t\t\t\tcolours[4]=Color.BLACK;\n\t\t\t\t\t\tfonts[5]=theApp.boldFont;\n\t\t\t\t\t\tcolours[5]=Color.BLACK;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Idle\n\t\t\t\t// Error check this to detect problems with the data stream\n\t\t\t\telse if (dataType==9)\t{\n\t\t\t\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\t\t\t\tBPTCres=bptc19696.decode(dibit_buf);\n\t\t\t\t\t// If we don't want to display these then clear the lines\n\t\t\t\t\tif (theApp.isDisplayIdlePDU()==false) shouldDisplay=false;\n\t\t\t\t}\t\t\t\t\n\t\t\t}\n\t\t\tif ((SLOT_TYPEres==true)&&(BPTCres==true)) return true;\n\t\t\telse return false;\n\t\t}\n\t}\n\t\n\t// Code to calculate all valid values for Quadratic residue (16,7,6)\n\tboolean calcQuadResidue1676 ()\t{\n\t\tboolean d[]=new boolean[7];\n\t\tboolean p[]=new boolean[9];\n\t\tint value[]=new int[128];\n\t\tint a;\n\t\t// Run through all possible 7 bit values\n\t\tfor (a=0;a<128;a++){\n\t\t\t// Convert to binary\n\t\t\tif ((a&64)>0) d[0]=true;\n\t\t\telse d[0]=false;\n\t\t\tif ((a&32)>0) d[1]=true;\n\t\t\telse d[1]=false;\n\t\t\tif ((a&16)>0) d[2]=true;\n\t\t\telse d[2]=false;\n\t\t\tif ((a&8)>0) d[3]=true;\n\t\t\telse d[3]=false;\n\t\t\tif ((a&4)>0) d[4]=true;\n\t\t\telse d[4]=false;\n\t\t\tif ((a&2)>0) d[5]=true;\n\t\t\telse d[5]=false;\n\t\t\tif ((a&1)>0) d[6]=true;\n\t\t\telse d[6]=false;\n\t\t\t// Shift the value 9 times to the left\n\t\t\tvalue[a]=a<<9;\n\t\t\t// Calculate the parity bits\n\t\t\tp[0]=d[1]^d[2]^d[3]^d[4];\n\t\t\tp[1]=d[2]^d[3]^d[4]^d[5];\n\t\t\tp[2]=d[0]^d[3]^d[4]^d[5]^d[6];\n\t\t\tp[3]=d[2]^d[3]^d[5]^d[6];\n\t\t\tp[4]=d[1]^d[2]^d[6];\n\t\t\tp[5]=d[0]^d[1]^d[4];\n\t\t\tp[6]=d[0]^d[1]^d[2]^d[5];\n\t\t\tp[7]=d[0]^d[1]^d[2]^d[3]^d[6];\n\t\t\tp[8]=d[0]^d[2]^d[4]^d[5]^d[6];\n\t\t\t// Add these to the lower bits of the valid words\n\t\t\tif (p[0]==true) value[a]=value[a]+256;\n\t\t\tif (p[1]==true) value[a]=value[a]+128;\n\t\t\tif (p[2]==true) value[a]=value[a]+64;\n\t\t\tif (p[3]==true) value[a]=value[a]+32;\n\t\t\tif (p[4]==true) value[a]=value[a]+16;\n\t\t\tif (p[5]==true) value[a]=value[a]+8;\n\t\t\tif (p[6]==true) value[a]=value[a]+4;\n\t\t\tif (p[7]==true) value[a]=value[a]+2;\n\t\t\tif (p[8]==true) value[a]=value[a]+1;\n\n\t\t}\n\t\t// Just something to break on !\n\t\treturn true;\n\t}\n\t\n\t// Check the EMB against a precomputed list of correct words\n\tboolean QuadResidue1676 (boolean[] word)\t{\n\t\tint a;\n\t\t// A complete list of valid slot type words\n\t\t// This was generated by the calcQuadResidue1676() method\n\t\tfinal int[]ResidueNums={0,627,1253,1686,2505,3002,3372,3935,4578,5009,5383,6004,6187,\n\t\t\t\t6744,7374,7869,8631,9156,9554,10017,10366,10765,11419,12008,12373,12838,13488, \n\t\t\t\t14019,14748,15343,15737,16138,16670,17261,17915,18312,18647,19108,19506,20033, \n\t\t\t\t20732,21135,21529,22122,22837,23366,24016,24483,24745,25306,25676,26175,26976, \n\t\t\t\t27411,28037,28662,29003,29496,30126,30685,30850,31473,31847,32276,32847,33340, \n\t\t\t\t33962,34521,35206,35829,36195,36624,37293,37854,38216,38715,39012,39447,40065, \n\t\t\t\t40690,41464,41867,42269,42862,43057,43586,44244,44711,45082,45673,46335,46732, \n\t\t\t\t47571,48032,48438,48965,49489,49954,50612,51143,51352,51947,52349,52750,53427, \n\t\t\t\t53952,54358,54821,55674,56073,56735,57324,57574,58005,58371,58992,59695,60252, \n\t\t\t\t60874,61369,61700,62327,62945,63378,63693,64190,64552,65115};\n\t\t// Convert the boolean array into an integer\n\t\tif (word[15]==true) residueValue=1;\n\t\telse residueValue=0;\n\t\tif (word[14]==true) residueValue=residueValue+2;\n\t\tif (word[13]==true) residueValue=residueValue+4;\n\t\tif (word[12]==true) residueValue=residueValue+8;\n\t\tif (word[11]==true) residueValue=residueValue+16;\n\t\tif (word[10]==true) residueValue=residueValue+32;\n\t\tif (word[9]==true) residueValue=residueValue+64;\n\t\tif (word[8]==true) residueValue=residueValue+128;\n\t\tif (word[7]==true) residueValue=residueValue+256;\n\t\tif (word[6]==true) residueValue=residueValue+512;\n\t\tif (word[5]==true) residueValue=residueValue+1024;\n\t\tif (word[4]==true) residueValue=residueValue+2048;\n\t\tif (word[3]==true) residueValue=residueValue+4096;\n\t\tif (word[2]==true) residueValue=residueValue+8192;\n\t\tif (word[1]==true) residueValue=residueValue+16384;\n\t\tif (word[0]==true) residueValue=residueValue+32768;\n\t\t// Run through the possible values and we have a match return true\n\t\tfor (a=0;a<128;a++)\t{\n\t\t\tif (residueValue==ResidueNums[a]) return true;\n\t\t}\n\t\t// No matches so we must have a problem and so should return false\n\t\treturn false;\n\t}\n\t\n\t// Return the fonts in use\n\tpublic Font[] getFonts()\t{\n\t\treturn fonts;\n\t}\n\t\n\t// Return the colours in use\n\tpublic Color[] getColours()\t{\n\t\treturn colours;\n\t}\n\t\n\t// Tells the main program if this PDU should be displayed\n\tpublic boolean getShouldDisplay()\t{\n\t\treturn shouldDisplay;\n\t}\n\t\n\tpublic void setShouldDisplay (boolean b)\t{\n\t\tshouldDisplay=b;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/DMRVoice.java",
    "content": "package com.dmr;\n\nimport java.awt.Color;\nimport java.awt.Font;\n\npublic class DMRVoice {\n\tprivate String line[]=new String[10];\n\tprivate boolean res;\n\tprivate Font fonts[]=new Font[10];\n\tprivate Color colours[]=new Color[10];\n\tprivate boolean shouldDisplay=true;\n\tprivate DMRDecode theApp;\n\t\n\tpublic String[] decode (DMRDecode tTheApp,byte[] dibit_buf)\t{\n\t\ttheApp=tTheApp;\n\t\tint mode=tTheApp.getMode();\n\t\tline[0]=theApp.getTimeStamp()+\" DMR Voice Frame \";\n\t\tif (mode==0) line[0]=line[0]+\" (BS)\";\n\t\telse if (mode==1) line[0]=line[0]+\" (MS)\";\n\t\telse if (mode==2) line[0]=line[0]+\" (Direct)\";\n\t\tfonts[0]=theApp.boldFont;\n\t\tcolours[0]=Color.BLACK;\n\t\t// BS only\n\t\tif (mode==0)\t{\n\t\t\tDecodeCACH cachdecode=new DecodeCACH();\n\t\t\t// CACH decode\n\t\t\tString cline=cachdecode.decode(theApp,dibit_buf);\n\t\t\tres=cachdecode.isPassErrorCheck();\n\t\t\tif (res==true)\t{\n\t\t\t\tline[1]=cline;\n\t\t\t\t// If short LC data is available then display it\n\t\t\t\tif (cachdecode.getShortLC()==true)\t{\n\t\t\t\t\tline[7]=cachdecode.getShortLCline();\n\t\t\t\t\tfonts[7]=theApp.boldFont;\n\t\t\t\t\tif (cachdecode.getshortLCError()==true)\t{\n\t\t\t\t\t\tcolours[7]=Color.RED;\n\t\t\t\t\t\tif (theApp.isDisplayOnlyGoodFrames()==true) line[7]=null;\n\t\t\t\t\t}\n\t\t\t\t\telse colours[7]=Color.BLACK;\n\t\t\t\t\tcachdecode.clearShortLC();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\t{\n\t\t\tres=true;\n\t\t}\n\t\t\n\t\t// Pass on voice data\n\t\tVoiceData voicedata=new VoiceData();\n\t\tvoicedata.handleVoice(tTheApp,dibit_buf);\n\t\ttheApp.frameCount++;\n\t\treturn line;\n\t}\n\n\tpublic boolean isError() {\n\t\treturn res;\n\t}\n\t\n\t// Return the fonts in use\n\tpublic Font[] getFonts()\t{\n\t\treturn fonts;\n\t}\n\t\n\t// Return the colours in use\n\tpublic Color[] getColours()\t{\n\t\treturn colours;\n\t}\n\t\n\t// Tells the main program if this PDU should be displayed\n\tpublic boolean getShouldDisplay()\t{\n\t\treturn shouldDisplay;\n\t}\n\t\n\tpublic void setShouldDisplay (boolean b)\t{\n\t\tshouldDisplay=b;\n\t}\n\t\n\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/DecodeCACH.java",
    "content": "package com.dmr;\n\npublic class DecodeCACH {\n\tprivate StringBuilder line=new StringBuilder(250);\n\tprivate String shortLCline;\n\tprivate boolean at;\n\tprivate boolean channel;\n\tprivate int lcss;\n\tprivate boolean passErrorCheck=false;\n\tprivate boolean haveShortLC=false;\n\tprivate boolean shortLCError=false;\n\tprivate int errorRes;\n\tprivate DMRDecode theApp;\n\t\n\tpublic String decode (DMRDecode TtheApp,byte[] dibit_buf)\t{\n\t\ttheApp=TtheApp;\n\t\tline.append(\"CACH : TACT \");\n\t\t// CACH decode\n\t\tpassErrorCheck=mainDecode(dibit_buf);\n\t\t// Only pass the decoded data back if the user wants to display CACH info\n\t\tif (theApp.isDisplayCACH()==true) return line.toString();\n\t\telse return null;\n\t}\n\t\n\t// De-interleave , CRC check and decode the CACH\n\t// With code added to work out which interleave sequence to use\n\tprivate boolean mainDecode (byte[] dibit_buf)\t{\n\t\tint a,r=0,fragType=-1;\n\t\tboolean rawdataCACH[]=new boolean[24];\n\t\tboolean dataCACH[]=new boolean[24];\n\t\tfinal int[]interleaveCACH={0,4,8,12,14,18,22,1,2,3,5,6,7,9,10,11,13,15,16,17,19,20,21,23};\t\n\t\t// Convert from dibits into boolean\n\t\tfor (a=0;a<12;a++)\t{\n\t\t\tif (dibit_buf[a]==0)\t{\n\t\t\t\trawdataCACH[r]=false;\n\t\t\t\trawdataCACH[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==1)\t{\n\t\t\t\trawdataCACH[r]=false;\n\t\t\t\trawdataCACH[r+1]=true;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==2)\t{\n\t\t\t\trawdataCACH[r]=true;\n\t\t\t\trawdataCACH[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==3)\t{\n\t\t\t\trawdataCACH[r]=true;\n\t\t\t\trawdataCACH[r+1]=true;\n\t\t\t}\n\t\t\tr=r+2;\n\t\t}\n\t\t// De-interleave\n\t\tfor (a=0;a<24;a++)\t{\n\t\t\tr=interleaveCACH[a];\n\t\t\tdataCACH[a]=rawdataCACH[r];\n\t\t}\n\t\t//  Convert the first 7 bits (TACT + 3 parity bits) into an integer\n\t\tif (dataCACH[0]==true) errorRes=64;\n\t\telse errorRes=0;\n\t\tif (dataCACH[1]==true) errorRes=errorRes+32;\n\t\tif (dataCACH[2]==true) errorRes=errorRes+16;\n\t\tif (dataCACH[3]==true) errorRes=errorRes+8;\n\t\tif (dataCACH[4]==true) errorRes=errorRes+4;\n\t\tif (dataCACH[5]==true) errorRes=errorRes+2;\n\t\tif (dataCACH[6]==true) errorRes++;\n\t\tif (errorCheckHamming743(errorRes)==false) return false;\n\t\t// Decode the TACT\n\t\tat=dataCACH[0];\n\t\tchannel=dataCACH[1];\n\t\tif (dataCACH[2]==true) lcss=2;\n\t\telse lcss=0;\n\t\tif (dataCACH[3]==true) lcss++;\n\t\t// Display TACT info\n\t\tif (at==true) line.append(\" AT=1\");\n\t\tif (channel==false)\t{\n\t\t\tline.append(\" Ch 1\");\n\t\t\ttheApp.currentChannel=1;\n\t\t}\n\t\telse\t{\n\t\t\tline.append(\" Ch 2\");\n\t\t\ttheApp.currentChannel=2;\n\t\t}\n\t\tif (lcss==0) line.append(\" First fragment of CSBK \");\n\t\telse if (lcss==1) line.append(\" First fragment of LC \");\n\t\telse if (lcss==2) line.append(\" Last fragment of LC \");\n\t\telse if (lcss==3) line.append(\" Continuation fragment of LC \");\n\t\t// If this is an short LC message pass the data on to the ShortLC object\n\t\tif (lcss==3) fragType=1;\n\t\telse if (lcss==2) fragType=2;\n\t\telse if (lcss==1) fragType=0;\n\t\t// Below is commented out as the code contains a known bug\n\t\t// Also other things need fixing first\n\t\tif (fragType!=-1) theApp.short_lc.addData(dataCACH,fragType);\n\t\t// Is short LC data ready ?\n\t\tif (theApp.short_lc.isDataReady()==true)\t{\n\t\t\t// See if the short LC passed its error checks\n\t\t\tif (theApp.short_lc.isCRCgood()==true)\t{\n\t\t\t\tshortLCError=false;\n\t\t\t\tshortLCline=theApp.getTimeStamp()+\" Short LC : \"+theApp.short_lc.getLine();\n\t\t\t}\n\t\t\telse\t{\n\t\t\t\tshortLCError=true;\n\t\t\t\tshortLCline=theApp.getTimeStamp()+\" Bad Short LC !\";\n\t\t\t}\n\t\t\ttheApp.short_lc.clrDataReady();\n\t\t\thaveShortLC=true;\n\t\t}\n\t\telse haveShortLC=false;\n\t\treturn true;\n\t}\n\t\n\t// Error check the CACH TACT\n\tpublic boolean errorCheckHamming743(int tact)\t{\n\t\t// An array of valid Hamming words which was generated by calcHamming()\n\t\tfinal int[]Hamming743={0,11,22,29,39,44,49,58,69,78,83,88,98,105,116,127};\n\t\tint a;\n\t\tfor (a=0;a<16;a++)\t{\n\t\t\t// If we have a match return true\n\t\t\tif (tact==Hamming743[a]) return true;\t\n\t\t}\n\t\t// If no match there is a problem so return a false\n\t\treturn false;\n\t}\n\t\n\t// Generate a list of valid Hamming words\n\t// Isn't normally called but leave in for now\n\tpublic boolean calcHamming ()\t{\n\t\tboolean d1,d2,d3,d4,h2,h1,h0;\n\t\tint a;\n\t\tint valid[]=new int[16];\n\t\t// Run through all possible values\n\t\tfor (a=0;a<16;a++)\t{\n\t\t\t// Covert from an integer to boolean\n\t\t\tif ((a&8)>0) d1=true;\n\t\t\telse d1=false;\n\t\t\tif ((a&4)>0) d2=true;\n\t\t\telse d2=false;\n\t\t\tif ((a&2)>0) d3=true;\n\t\t\telse d3=false;\n\t\t\tif ((a&1)>0) d4=true;\n\t\t\telse d4=false;\n\t\t\t// Calculate the parity bits\n\t\t\th2=d1^d2^d3;\n\t\t\th1=d2^d3^d4;\n\t\t\th0=d1^d2^d4;\n\t\t\t// Convert this back into a 7 bit integer\n\t\t\tif (d1==true) valid[a]=64;\n\t\t\telse valid[a]=0;\n\t\t\tif (d2==true) valid[a]=valid[a]+32;\n\t\t\tif (d3==true) valid[a]=valid[a]+16;\n\t\t\tif (d4==true) valid[a]=valid[a]+8;\n\t\t\tif (h2==true) valid[a]=valid[a]+4;\n\t\t\tif (h1==true) valid[a]=valid[a]+2;\n\t\t\tif (h0==true) valid[a]=valid[a]+1;\t\t\n\t\t}\n\t\t// Just something to break on\n\t\treturn true;\n\t}\n\n\t// Let the main program know if there is an error in the frame\n\tpublic boolean isPassErrorCheck() {\n\t\treturn passErrorCheck;\n\t}\n\n\t// Let the main program know the hamming word in case of an error\n\tpublic int getErrorRes() {\n\t\treturn errorRes;\n\t}\t\n\t\n\t// Tell the user we have a Short LC\n\tpublic boolean getShortLC()\t{\n\t\treturn haveShortLC;\n\t}\n\t\n\t// Clear the Short LC variables\n\tpublic void clearShortLC()\t{\n\t\thaveShortLC=false;\n\t}\n\t\n\t// Return the decoded short LC\n\tpublic String getShortLCline()\t{\n\t\treturn shortLCline;\n\t}\n\t\n\tpublic boolean getshortLCError()\t{\n\t\treturn shortLCError;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/DisplayBar.java",
    "content": "package com.dmr;\n\nimport java.awt.*;\nimport javax.swing.*;\nimport javax.swing.border.Border;\n\npublic class DisplayBar extends JPanel {\n\tpublic static final long serialVersionUID=1;\n\tprivate Border loweredbevel=BorderFactory.createLoweredBevelBorder();\n\tstatic final int BUFFERMAX=75;\n\tprivate int bufferCounter=0;\n\tprivate int symbolBuffer[]=new int[BUFFERMAX];\n\tprivate int max,min,umid,lmid,fullRange;\n\tprivate boolean displayActive=false,enableDisplay=false;\n\t\n\tpublic DisplayBar () {\n\t\tthis.setBorder(loweredbevel);\n\t}\n\t\n\t// Draw the display in the JPanel\n\t@Override public void paintComponent(Graphics g) {\n\t\tint a,val;\n\t\tint height=this.getHeight();\n\t\tint width=this.getWidth();\n\t\tdouble modFactor=((float)height/(float)fullRange);\n\t\t// Repaint the background\n\t\tsuper.paintComponent(g);  \n\t\t// If the display isn't active then don't go any further\n\t\tif ((displayActive==false)||(enableDisplay==false)) return;\n        // Draw the centre line \n        g.drawLine(0,(height/2),width,(height/2));\n        // Draw the lmid line\n        val=lmid+Math.abs(min);\n        val=(int)((float)val*modFactor);\n        g.drawLine(0,val,width,val);\n        // Draw the lmid line\n        val=umid+Math.abs(min);\n        val=(int)((float)val*modFactor);\n        g.drawLine(0,val,width,val);\n        // Draw the symbol points\n        for (a=0;a<BUFFERMAX;a++)\t{\n        \tval=symbolBuffer[a]+Math.abs(min);\n        \tval=(int)((float)val*modFactor);\n          \tg.fillRect((width/2)-2,val,5,5);\n        }\n   }\n\t\n\t// Add a symbol to a circular buffer which is displayed\n\tpublic void addToBuffer (int tsymbol)\t{\n\t\tsymbolBuffer[bufferCounter]=tsymbol;\n\t\tbufferCounter++;\n\t\t// Repaint every 25 samples\n\t\tif ((bufferCounter%25)==0) repaint();\n\t\t// Check if the circular buffer counter has reached its maximum\n\t\tif (bufferCounter==BUFFERMAX) bufferCounter=0;\t\n\t}\n\t\n\t// Set the displays parameters\n\tpublic void setDisplayBarParams (int tmax,int tmin,int tumid,int tlmid)\t{\n\t\tmax=tmax;\n\t\tmin=tmin;\n\t\tumid=tumid;\n\t\tlmid=tlmid;\n\t\t// Calculate the full range needed\n\t\tfullRange=Math.abs(max)+Math.abs(min);\n\t\t// Enable the display and do a repaint\n\t\tdisplayActive=true;\n\t\trepaint();\n\t}\n\t\n\t// Stop the display if sync is lost\n\tpublic void stopDisplay()\t{\n\t\tdisplayActive=false;\n\t\trepaint();\n\t}\n\t\n\t// Enable or disable the display\n\tpublic void setEnableDisplay (boolean val)\t{\n\t\tenableDisplay=val;\n\t\trepaint();\n\t}\n\n\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/DisplayFrame.java",
    "content": "// Please note much of the code in this program was taken from the DSD software\n// and converted into Java. The author of this software is unknown but has the\n// GPG Key ID below\n\n// Copyright (C) 2010 DSD Author\n// GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6  F630 FAA2 635D 3F1D 7FD0)\n// \n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND ISC DISCLAIMS ALL WARRANTIES WITH\n// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,\n// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE\n// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n// PERFORMANCE OF THIS SOFTWARE.\n//\n\npackage com.dmr;\n\nimport java.awt.*;\nimport javax.swing.*;\nimport java.awt.datatransfer.StringSelection;\nimport java.awt.event.*;\nimport java.io.*;\nimport java.text.DecimalFormat;\nimport java.util.ArrayList;\nimport javax.sound.sampled.AudioSystem;\nimport javax.sound.sampled.Line;\nimport javax.sound.sampled.Mixer;\n\npublic class DisplayFrame extends JFrame implements ActionListener {\n\tprivate JMenuBar menuBar=new JMenuBar();\n\tprivate DMRDecode theApp;\n\tpublic static final long serialVersionUID=1;\n\tprivate JMenuItem save_to_file,inverted_item,debug_item,capture_item,quick_log,save_settings;\n\tprivate JMenuItem error_rate,exit_item,about_item,help_item,view_display_bar;\n\tprivate JMenuItem view_cach,view_idle,view_onlygood,view_voice;\n\tprivate JMenuItem clear_screen,copy_screen,twitter_item,download_item,linkedin_item;\n\tprivate JStatusBar statusBar=new JStatusBar();\n\tprivate DisplayBar displayBar=new DisplayBar();\n\tpublic JScrollBar vscrollbar=new JScrollBar(JScrollBar.VERTICAL,0,1,0,2000);\n\tprivate JMenu audioDevicesMenu;\n\tprivate static ArrayList<AudioMixer> devices;\n\n\t// Constructor\n\tpublic DisplayFrame(String title,DMRDecode theApp) {\n\t\tsetTitle(title);\n\t\tthis.theApp=theApp;\n\t\tsetDefaultCloseOperation(EXIT_ON_CLOSE);\n\t\tgetContentPane().setBackground(Color.WHITE);\n\t\t// Menu setup\n\t\tsetJMenuBar(menuBar);\n\t\t// Main\n\t\tJMenu mainMenu=new JMenu(\"Main\");\n\t\tmainMenu.add(capture_item=new JRadioButtonMenuItem(\"Capture\",theApp.isCapture()));\n\t\t// Disable the capture radio button\n\t\tcapture_item.setEnabled(false);\n\t\tcapture_item.addActionListener(this);\n\t\tmainMenu.add(debug_item=new JRadioButtonMenuItem(\"Debug Mode\",theApp.isDebug()));\n\t\t// Disable the debug radio button\n\t\tdebug_item.setEnabled(false);\n\t\tdebug_item.addActionListener(this);\n\t\tmainMenu.add(inverted_item=new JRadioButtonMenuItem(\"Invert Signal\",theApp.inverted));\n\t\tinverted_item.addActionListener(this);\n\t\tmainMenu.add(quick_log=new JRadioButtonMenuItem(\"Quick Log\",theApp.isQuickLog()));\n\t\tquick_log.addActionListener(this);\n\t\tmainMenu.add(save_settings=new JMenuItem(\"Save Settings\"));\n\t\tsave_settings.addActionListener(this);\n\t\tmainMenu.add(save_to_file=new JRadioButtonMenuItem(\"Save to File\",theApp.getLogging()));\n\t\tsave_to_file.addActionListener(this);\n\t\tmainMenu.add(exit_item=new JMenuItem(\"Exit\"));\t\t\n\t\texit_item.addActionListener(this);\n\t\tmenuBar.add(mainMenu);\n\t\t// Audio \n\t\tJMenu audioMenu=new JMenu(\"Audio\");\n\t\taudioDevicesMenu=buildAudioDevices();\n\t\taudioMenu.add(audioDevicesMenu);\n\t\taudioDevicesMenu.updateUI();\n\t\tmenuBar.add(audioMenu);\n\t\t// Info\n\t\tJMenu infoMenu=new JMenu(\"Info\");\n\t\tinfoMenu.add(view_display_bar=new JRadioButtonMenuItem(\"Enable Symbol Display\",theApp.isEnableDisplayBar()));\n\t\tview_display_bar.addActionListener(this);\n\t\tinfoMenu.add(error_rate=new JMenuItem(\"Error Check Info\"));\t\t\n\t\terror_rate.addActionListener(this);\n\t\tmenuBar.add(infoMenu);\n\t\t// View\n\t\tJMenu viewMenu=new JMenu(\"View\");\n\t\tviewMenu.add(copy_screen=new JMenuItem(\"Copy All to the Clipboard\"));\n\t\tcopy_screen.addActionListener(this);\n\t\tviewMenu.add(clear_screen=new JMenuItem(\"Clear Screen\"));\n\t\tclear_screen.addActionListener(this);\n\t\tviewMenu.addSeparator();\n\t\tviewMenu.add(view_cach=new JRadioButtonMenuItem(\"Display CACH\",theApp.isDisplayCACH()));\n\t\tview_cach.addActionListener(this);\n\t\tviewMenu.add(view_onlygood=new JRadioButtonMenuItem(\"Display Good Frames Only\",theApp.isDisplayOnlyGoodFrames()));\n\t\tview_onlygood.addActionListener(this);\n\t\tviewMenu.add(view_idle=new JRadioButtonMenuItem(\"Display Idle PDU\",theApp.isDisplayIdlePDU()));\n\t\tview_idle.addActionListener(this);\n\t\tviewMenu.add(view_voice=new JRadioButtonMenuItem(\"Display Voice Frames\",theApp.isDisplayVoiceFrames()));\n\t\tview_voice.addActionListener(this);\n\t\tmenuBar.add(viewMenu);\n\t\t// Help\n\t\tJMenu helpMenu=new JMenu(\"Help\");\n\t\thelpMenu.add(about_item=new JMenuItem(\"About\"));\t\t\n\t\tabout_item.addActionListener(this);\n\t\thelpMenu.add(linkedin_item=new JMenuItem(\"Connect on Linkedin\"));\t\t\n\t\tlinkedin_item.addActionListener(this);\n\t\thelpMenu.add(download_item=new JMenuItem(\"Download the latest build of DMRDecode\"));\t\t\n\t\tdownload_item.addActionListener(this);\n\t\thelpMenu.add(twitter_item=new JMenuItem(\"Follow DMRDecode Progress on Twitter\"));\t\t\n\t\ttwitter_item.addActionListener(this);\n\t\thelpMenu.add(help_item=new JMenuItem(\"Help\"));\t\t\n\t\thelp_item.addActionListener(this);\t\t\n\t\tmenuBar.add(helpMenu);\n\t\t// Setup the status bar\n\t\tgetContentPane().add(statusBar, java.awt.BorderLayout.SOUTH);\n\t\tstatusBar.setLoggingStatus(\"Not Logging\");\n\t\tstatusBar.setApp(theApp);\n\t\t// Setup the display bar\n\t\tgetContentPane().add(displayBar, java.awt.BorderLayout.WEST);\n\t\t// Add the vertical scrollbar\n\t\tadd(vscrollbar,BorderLayout.EAST);\n\t\t// Add a listener for this\n\t\tvscrollbar.addAdjustmentListener(new MyAdjustmentListener());\n\t\t// Read in the default settings file\n\t\ttry\t{\n\t\t\ttheApp.readDefaultSettings();\n\t\t\t// Update the menus\n\t\t\tmenuItemUpdate();\n\t\t}\n\t\tcatch (Exception e)\t{\n\t\t\tString err=e.toString();\n\t\t\t// Can't find the default settings file //\n\t\t\tSystem.out.println(\"\\nInformative : Unable to read the file DMRDecode_settings.xml \"+err);\n\t\t}\n\t\t\t\n\t\t}\n\n\t\n\t// Handle messages from the scrollbars\n\tclass MyAdjustmentListener implements AdjustmentListener  {\n\t\tpublic void adjustmentValueChanged(AdjustmentEvent e) {\n\t\t\t// Vertical scrollbar\n\t\t\tif (e.getSource()==vscrollbar) {\n\t\t\t\ttheApp.vertical_scrollbar_value=e.getValue();\n\t\t\t\trepaint();   \n\t\t\t}\n\t\t\t\n\t\t}\n\t }\n\n\n\t// Handle all menu events\n\tpublic void actionPerformed (ActionEvent event) {\n\t\tString event_name=event.getActionCommand();\n\t\t\n\t\t// About\n\t\tif (event_name==\"About\")\t{\n\t\t\tString line=theApp.program_version+\"\\r\\n\"+\"Ian Wraith (ianwraith@gmail.com)\\r\\nalso big thanks to Chris Sams for the CPU utilization fix \" +\n\t\t\t\t\t\"\\r\\nplus thanks to Andy W for the mixer and audio code\" +\n\t\t\t\t\t\"\\r\\nalso to the author of DSD from whom I took most of the demodulation code.\";\n\t\t\tJOptionPane.showMessageDialog(null,line,\"DMRDecode\", JOptionPane.INFORMATION_MESSAGE);\n\t\t}\n\t\t\n\t\t// Capture\n\t\tif (event_name==\"Capture\")\t{\n\t\t\tif (theApp.isCapture()==false) theApp.setCapture(true);\n\t\t\telse theApp.setCapture(false);\n\t\t}\n\t\t\n\t\t// Clear Screen\n\t\tif (event_name==\"Clear Screen\") theApp.clearScreen();\n\t\t\n\t\t// Copy to the clip board\n\t\tif (event_name==\"Copy All to the Clipboard\") setClipboard(theApp.getAllText());\n\t\t\n\t\t// Debug Mode\n\t\tif (event_name==\"Debug Mode\")\t{\n\t\t\tif (theApp.isDebug()==false) theApp.setDebug(true);\n\t\t\t else theApp.setDebug(false);\n\t\t}\t\t\n\n\t\t// Invert signal\n\t\tif (event_name==\"Invert Signal\")\t{\n\t\t\tif (theApp.inverted==false) theApp.inverted=true;\n\t\t\t else theApp.inverted=false;\n\t\t}\n\t\t\n\t\t// Quick Log\n\t\tif (event_name==\"Quick Log\")\t{\n\t\t\tif (theApp.isQuickLog()==false)\t{\n\t\t\t\tquickLogDialogBox();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcloseQuickLogFile();\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Save to File\n\t\tif (event_name==\"Save to File\")\t{\t\t\n\t\t\tif (theApp.getLogging()==false)\t{\n\t\t\t\tif (saveDialogBox()==false)\t{\n\t\t\t\t\t// Restart the audio in thread\n\t\t\t\t\ttheApp.lineInThread.startAudio();\n\t\t\t\t\tmenuItemUpdate();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttheApp.setLogging(true);\n\t\t\t\tstatusBar.setLoggingStatus(\"Logging\");\n\t\t\t}\n\t\t\t else\t{\n\t\t\t\t closeLogFile();\n\t\t\t }\n\t\t\t// Restart the audio in thread\n\t\t\ttheApp.lineInThread.startAudio();\n\t\t}\t\n\t\t\n\t\t// Save the current settings\n\t\tif (event_name==\"Save Settings\") theApp.saveCurrentSettings();\n\t\t\n\t\t// Twitter\n\t\tif (event_name==\"Follow DMRDecode Progress on Twitter\")\t{\n\t\t\tBareBonesBrowserLaunch.openURL(\"https://twitter.com/#!/IanWraith\");\n\t\t}\n\t\t\n\t\t// Latest build download\n\t\tif (event_name==\"Download the latest build of DMRDecode\")\t{\n\t\t\tBareBonesBrowserLaunch.openURL(\"http://borg.shef.ac.uk/dmrdecode/\");\n\t\t}\n\t\t\n\t\t// Error rate info\n\t\tif (event_name==\"Error Check Info\")\t{\n\t\t\terrorDialogBox();\n\t\t}\n\t\t\n\t\t// Enable/Disable the symbol display\n\t\tif (event_name==\"Enable Symbol Display\")\t{\n\t\t\tboolean estate=theApp.isEnableDisplayBar();\n\t\t\tif (estate==true) estate=false;\n\t\t\telse estate=true;\n\t\t\ttheApp.setEnableDisplayBar(estate);\n\t\t}\n\t\t\n\t\t// Exit \n\t\tif (event_name==\"Exit\") {\n\t\t\t// If logging close the file\n\t\t\tif (theApp.getLogging()==true) closeLogFile();\n\t\t\t// Close the audio down //\n\t\t\ttheApp.lineInThread.shutDownAudio();\n\t\t\t// Stop the program //\n\t\t\tSystem.exit(0);\t\n\t\t}\n\t\t\n\t\t// Help\n\t\tif (event_name==\"Help\") {\n\t\t\tBareBonesBrowserLaunch.openURL(\"https://github.com/IanWraith/DMRDecode/wiki\");\n\t\t}\n\t\t\n\t\t// Display CACH\n\t\tif (event_name==\"Display CACH\")\t{\n\t\t\tif (theApp.isDisplayCACH()==true) theApp.setDisplayCACH(false);\n\t\t\telse theApp.setDisplayCACH(true);\n\t\t\t// If logging update the log of this change in filter settings\n\t\t\tif (theApp.getLogging()==true)\t{\n\t\t\t\tif (theApp.isDisplayCACH()==false) theApp.fileWrite(\"Filter settings changed so CACH data isn't displayed\");\n\t\t\t\telse theApp.fileWrite(\"Filter settings changed so that CACH data is displayed\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Display Idle PDU\n\t\tif (event_name==\"Display Idle PDU\")\t{\n\t\t\tif (theApp.isDisplayIdlePDU()==true) theApp.setDisplayIdlePDU(false);\n\t\t\telse theApp.setDisplayIdlePDU(true);\n\t\t\t// If logging update the log of this change in filter settings\n\t\t\tif (theApp.getLogging()==true)\t{\n\t\t\t\tif (theApp.isDisplayIdlePDU()==false) theApp.fileWrite(\"Filter settings changed so Idle PDUs aren't displayed\");\n\t\t\t\telse theApp.fileWrite(\"Filter settings changed so that Idle PDUs are displayed\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Display only good frames\n\t\tif (event_name==\"Display Good Frames Only\")\t{\n\t\t\tif (theApp.isDisplayOnlyGoodFrames()==true) theApp.setDisplayOnlyGoodFrames(false);\n\t\t\telse theApp.setDisplayOnlyGoodFrames(true);\n\t\t\t// If logging update the log of this change in filter settings\n\t\t\tif (theApp.getLogging()==true)\t{\n\t\t\t\tif (theApp.isDisplayOnlyGoodFrames()==false) theApp.fileWrite(\"Filter settings changed so that frames with errors are displayed\");\n\t\t\t\telse theApp.fileWrite(\"Filter settings changed so that only frames without errors are displayed\");\n\t\t\t}\n\n\t\t}\n\t\t\n\t\t// Display Voice Frames\n\t\tif (event_name==\"Display Voice Frames\")\t{\n\t\t\tif (theApp.isDisplayVoiceFrames()==true) theApp.setDisplayVoiceFrames(false) ;\n\t\t\telse theApp.setDisplayVoiceFrames(true);\n\t\t\t// If logging update the log of this change in filter settings\n\t\t\tif (theApp.getLogging()==true)\t{\n\t\t\t\tif (theApp.isDisplayVoiceFrames()==false) theApp.fileWrite(\"Filter settings changed so voice frames aren't displayed\");\n\t\t\t\telse theApp.fileWrite(\"Filter settings changed so that voice frames are displayed\");\n\t\t\t}\n\n\t\t}\n\t\t\n\t\t// Change mixer\n\t\tif (event_name.equalsIgnoreCase(\"mixer\")){\n\t\t\tchangeMixer(((JRadioButtonMenuItem)event.getSource()).getText());\n\t\t}\n\t\t\n\t\t// Linkedin\n\t\tif (event_name==\"Connect on Linkedin\")\t{\n\t\t\tBareBonesBrowserLaunch.openURL(\"https://uk.linkedin.com/pub/dir/Ian/Wraith\");\n\t\t}\n\n\t\tmenuItemUpdate();\n\t}\n\n\t// Update all the menu items \n\tpublic void menuItemUpdate () {\n\t\tinverted_item.setSelected(theApp.inverted);\n\t\t//debug_item.setSelected(theApp.isDebug());\n\t\tsave_to_file.setSelected(theApp.getLogging());\n\t\tquick_log.setSelected(theApp.isQuickLog());\n\t\tview_cach.setSelected(theApp.isDisplayCACH());\n\t\tview_idle.setSelected(theApp.isDisplayIdlePDU());\n\t\tview_voice.setSelected(theApp.isDisplayVoiceFrames());\n\t\tview_onlygood.setSelected(theApp.isDisplayOnlyGoodFrames());\n\t\tview_display_bar.setSelected(theApp.isEnableDisplayBar());\n\t\t//capture_item.setSelected(theApp.isCapture());\n\t\t\n\t\t// Audio sources\n\t\tMenuElement[] devs=audioDevicesMenu.getSubElements();\n\t\tif (devs.length>0){\n\t\t\t\tfor (MenuElement m : devs[0].getSubElements()){\n\t\t\t\t\tif (((JRadioButtonMenuItem)m).getText().equals(theApp.lineInThread.getMixerName())){\n\t\t\t\t\t\t((JRadioButtonMenuItem)m).setSelected(true);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t}\n\t\n\t// Display a dialog box so the user can select a location and name for a log file\n\tpublic boolean saveDialogBox ()\t{\n\t\tif (theApp.getLogging()==true) return false;\n\t\tString file_name;\n\t\tBoolean append=true;\n\t\t// Bring up a dialog box that allows the user to select the name\n\t\t// of the saved file\n\t\tJFileChooser fc=new JFileChooser();\n\t\t// The dialog box title //\n\t\tfc.setDialogTitle(\"Select the log file name\");\n\t\t// Start in current directory\n\t\tfc.setCurrentDirectory(new File(\".\"));\n\t\t// Don't all types of file to be selected //\n\t\tfc.setAcceptAllFileFilterUsed(false);\n\t\t// Only show .txt files //\n\t\tfc.setFileFilter(new TextfileFilter());\n\t\t// Show save dialog; this method does not return until the\n\t\t// dialog is closed\n\t\tint returnval=fc.showSaveDialog(this);\n\t\t// If the user has selected cancel then quit\n\t\tif (returnval==JFileChooser.CANCEL_OPTION) return false;\n\t\t// Get the file name an path of the selected file\n\t\tfile_name=fc.getSelectedFile().getPath();\n\t\t// Does the file name end in .txt ? //\n\t\t// If not then automatically add a .txt ending //\n\t\tint last_index=file_name.lastIndexOf(\".txt\");\n\t\tif (last_index!=(file_name.length()-4)) file_name=file_name + \".txt\";\n\t\t// Create a file with this name //\n\t\tFile tfile=new File(file_name);\n\t\t// If the file exists ask the user if they want to overwrite it\n\t\tif (tfile.exists()) {\n\t\t\t// TODO : Fix the wording of the open log file dialog box e.g Have the buttons labelled \"Overwrite\" and \"Append\"\n\t\t\tint response=JOptionPane.showConfirmDialog(null,\n\t\t\t\t\t\"This log file already exists : What do you wish to do ?\\nClick Yes to overwrite it\\nClick No to append data to it\\nClick Cancel to quit\", \"Confirm Overwrite\",\n\t\t\t\t\tJOptionPane.YES_NO_CANCEL_OPTION,\n\t\t\t\t\tJOptionPane.QUESTION_MESSAGE);\n\t\t\tif (response==JOptionPane.CANCEL_OPTION) return false;\n\t\t\telse if (response==JOptionPane.YES_OPTION) append=false;\n\t\t}\n\t\t// Open the file\n\t\ttry {\n\t\t\t// If append==true then the data written is appended to this file\n\t\t\ttheApp.file=new FileWriter(tfile,append);\n\t\t\t// Clear all logged info\n\t\t\ttheApp.usersLogged.clearAll();\n\t\t\t// Write the program version as the first line of the log\n\t\t\tString fline=\"\\r\\n##########################################################\\r\\n\\r\\n\"+theApp.program_version+\"\\r\\n\";\n\t\t\ttheApp.file.write(fline);\n\t\t\t// Display the state of the filters at the start of the log\n\t\t\tif (theApp.isDisplayCACH()==false) theApp.file.write(\"You have selected not to display CACH data\\r\\n\");\n\t\t\tif (theApp.isDisplayOnlyGoodFrames()==true) theApp.file.write(\"You have selected only to display frames without errors\\r\\n\");\n\t\t\tif (theApp.isDisplayIdlePDU()==false) theApp.file.write(\"You have selected not to display Idle PDUs\\r\\n\");\n\t\t\tif (theApp.isDisplayVoiceFrames()==false) theApp.file.write(\"You have selected not to display voice frames\\r\\n\");\n\t\t\t\n\t\t} catch (Exception e) {\n\t\t\tSystem.out.println(\"\\nError opening the logging file\");\n\t\t\treturn false;\n\t\t}\n\t\ttheApp.setLogging(true);\n\t\treturn true;\n\t}\n\t\n\t// Close the log file\n\tpublic void closeLogFile()\t{\n\t\t int a,count;\n\t\t String line;\n\t\t theApp.setLogging(false);\n\t\t statusBar.setLoggingStatus(\"Not Logging\");\n\t\t try\t{\n\t\t\t // Display users\n\t\t\t count=theApp.usersLogged.returnUserCounter();\n\t\t\t // No users\n\t\t\t if (count==0)\t{\n\t\t\t\t theApp.file.write(\"\\r\\n\\r\\nNo users were logged\");\n\t\t\t }\n\t\t\t else\t{\n\t\t\t\t line=\"\\r\\n\\r\\nThe following \"+Integer.toString(count)+\" users were logged ..\";\n\t\t\t\t theApp.file.write(line);\n\t\t\t\t // Sort the users\n\t\t\t\t theApp.usersLogged.sortByIdent();\n\t\t\t\t // Run through each user\n\t\t\t\t for (a=0;a<count;a++)\t{\n\t\t\t\t\t line=\"\\r\\n\"+theApp.usersLogged.returnInfo(a);\n\t\t\t\t\t theApp.file.write(line);\n\t\t\t\t }\n\t\t\t }\n\t\t\t // Close the file\n\t\t\t theApp.file.flush();\n\t\t\t theApp.file.close();\n\t\t }\n\t\t catch (Exception e)\t{\n\t\t\t JOptionPane.showMessageDialog(null,\"Error closing Log file\",\"DMRDecode\", JOptionPane.INFORMATION_MESSAGE);\n\t\t }\n\t}\n\t\n\t// Display the percentage of bad frames received\n\tpublic void errorDialogBox()\t{\n\t\tString line;\n\t\tif (theApp.frameCount==0)\t{\n\t\t\tline=\"No frames received yet !\";\n\t\t}\n\t\telse\t{\n\t\t\tDecimalFormat df=new DecimalFormat(\"#.#\");\n\t\t\tdouble err=((double)theApp.badFrameCount/(double)theApp.frameCount)*100.0;\n\t\t\tline=df.format(err)+\"% of frames were bad.\";\n\t\t}\n\t\tJOptionPane.showMessageDialog(null,line,\"DMRDecode\", JOptionPane.INFORMATION_MESSAGE);\n\t}\n\t\n\t// Display a dialog box so the user can select a location and name for a quick log file\n\tpublic boolean quickLogDialogBox ()\t{\n\t\tif (theApp.isQuickLog()==true) return false;\n\t\tString file_name;\n\t\tBoolean append=true;\n\t\t// Bring up a dialog box that allows the user to select the name\n\t\t// of the saved file\n\t\tJFileChooser fc=new JFileChooser();\n\t\t// The dialog box title //\n\t\tfc.setDialogTitle(\"Select the quick log file name\");\n\t\t// Start in current directory\n\t\tfc.setCurrentDirectory(new File(\".\"));\n\t\t// Don't all types of file to be selected //\n\t\tfc.setAcceptAllFileFilterUsed(false);\n\t\t// Only show .csv files //\n\t\tfc.setFileFilter(new CSVFileFilter());\n\t\t// Show save dialog; this method does not return until the\n\t\t// dialog is closed\n\t\tint returnval=fc.showSaveDialog(this);\n\t\t// If the user has selected cancel then quit\n\t\tif (returnval==JFileChooser.CANCEL_OPTION) return false;\n\t\t// Get the file name an path of the selected file\n\t\tfile_name=fc.getSelectedFile().getPath();\n\t\t// Does the file name end in .csv ? //\n\t\t// If not then automatically add a .csv ending //\n\t\tint last_index=file_name.lastIndexOf(\".csv\");\n\t\tif (last_index!=(file_name.length()-4)) file_name=file_name + \".csv\";\n\t\t// Create a file with this name //\n\t\tFile tfile=new File(file_name);\n\t\t// If the file exists ask the user if they want to overwrite it or append to the existing file\n\t\tif (tfile.exists()) {\n\t\t\t// TODO : Fix the wording of this dialog box e.g Have the buttons labelled \"Overwrite\" and \"Append\"\n\t\t\tint response=JOptionPane.showConfirmDialog(null,\n\t\t\t\t\t\"This file already exists : What do you wish to do ?\\nClick Yes to overwrite it\\nClick No to append data to it\\nClick Cancel to quit\", \"Confirm Overwrite\",\n\t\t\t\t\tJOptionPane.YES_NO_CANCEL_OPTION,\n\t\t\t\t\tJOptionPane.QUESTION_MESSAGE);\n\t\t\tif (response==JOptionPane.CANCEL_OPTION) return false;\n\t\t\telse if (response==JOptionPane.YES_OPTION) append=false;\n\t\t}\n\t\t// Open the file\n\t\ttry {\n\t\t\t// If append==true then the data written is appended to this file\n\t\t\ttheApp.quickLogFile=new FileWriter(tfile,append);\n\t\t\t\n\t\t} catch (Exception e) {\n\t\t\tSystem.out.println(\"\\nError opening the quick log file\");\n\t\t\treturn false;\n\t\t}\n\t\ttheApp.setQuickLog(true);\n\t\treturn true;\n\t}\n\t\n\tpublic void closeQuickLogFile()\t{\n\t\ttry\t{\n\t\t\t// Close the file\n\t\t\t theApp.quickLogFile.flush();\n\t\t\t theApp.quickLogFile.close();\n\t\t} catch (Exception e)\t{\n\t\t\tJOptionPane.showMessageDialog(null,\"Error closing Quick Log file\",\"DMRDecode\", JOptionPane.INFORMATION_MESSAGE);\n\t\t}\n\t\ttheApp.setQuickLog(false);\n\t}\n\t\n\t// Set the volume indicating progress bar //\n\tpublic void updateVolumeBar(int val) {\n\t\t// Calculate as a percentage of 18000 (the max value)\n\t\tint pval=(int)(((float)val/(float)18000.0)*(float)100);\n\t\tstatusBar.setVolumeBar(pval);\n\t}\n\t\n\t// Update the sync label\n\tpublic void updateSyncLabel (boolean sync)\t{\n\t\tstatusBar.setSyncLabel(sync);\n\t}\n\t\n\t// Pass a symbol to the display bar symbol buffer\n\tpublic void displaySymbol (int tsymb)\t{\n\t\tdisplayBar.addToBuffer(tsymb);\n\t}\n\t\n\t// Set the display bar parameters\n\tpublic void displayBarParams (int tmax,int tmin,int tumid,int tlmid)\t{\n\t\tdisplayBar.setDisplayBarParams(tmax,tmin,tumid,tlmid);\n\t}\n\t\n\t// Stop the display bar \n\tpublic void stopDisplayBar()\t{\n\t\tdisplayBar.stopDisplay();\n\t}\n\t\n\t// Enable or disable the display bar\n\tpublic void switchDisplayBar (boolean st)\t{\n\t\tdisplayBar.setEnableDisplay(st);\n\t}\n\t\n\tpublic void setCh1Label (String label,Color col)\t{\n\t\tstatusBar.setCh1Label(label,col);\n\t}\n\t\n\tpublic void setCh2Label (String label,Color col)\t{\n\t\tstatusBar.setCh2Label(label,col);\n\t}\n\t\n\tpublic void SetColourCodeLabel (int cc,Color col)\t{\n\t\tstatusBar.setColourCodeLabel(cc,col);\n\t}\n\t\n\tpublic void setSystemLabel (String txt,Color col)\t{\n\t\tstatusBar.setSystemLabel(txt,col);\n\t}\n\t\n\t// This sets the clipboard with a string passed to it\n\tprivate void setClipboard(String str) {\n\t    StringSelection ss=new StringSelection(str);\n\t    Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss,null);\n\t}\n\t\n\tprivate JMenu buildAudioDevices(){\n\t\tJMenu ret=new JMenu(\"Audio Devices\");\n\t\tButtonGroup group=new ButtonGroup();\n\t\tArrayList<AudioMixer> deviceList=getCompatibleDevices();\n\t\tint i;\n\t\tfor (i=0; i<deviceList.size(); i++){\n\t\t\t//Line.Info l[]=AudioSystem.getTargetLineInfo(deviceList.get(i).lineInfo);\n\t\t\tJRadioButtonMenuItem dev=new JRadioButtonMenuItem(deviceList.get(i).description);\n\t\t\tdev.setActionCommand(\"mixer\");\n\t\t\tdev.addActionListener(this);\n\t\t\tif (i==0) dev.setSelected(true);\n\t\t\tgroup.add(dev);\n\t\t\tret.add(dev);\n\t\t}\n\t\treturn ret;\n\t}\n\t\n\tprivate ArrayList<AudioMixer> getCompatibleDevices(){\n\t\tdevices=new ArrayList<AudioMixer>();\n\t\t//list the available mixers\n\t\tMixer.Info mixers[]=AudioSystem.getMixerInfo();\n\t\tint i;\n\t\t//iterate the mixers and display TargetLines\n\t\tfor (i=0;i<mixers.length;i++){\n\t\t\tMixer m = AudioSystem.getMixer(mixers[i]);\n\t\t\tLine.Info l[]=m.getTargetLineInfo();\n\t\t\tif(l.length>0){\n\t\t\t\tint x;\n\t\t\t\tfor (x=0;x<l.length;x++){\n\t\t\t\t\tif (l[0].getLineClass().getName().equals(\"javax.sound.sampled.TargetDataLine\")){\n\t\t\t\t\t\tAudioMixer mc=new AudioMixer(this.theApp,mixers[i].getName(),m,l[x]);\n\t\t\t\t\t\tdevices.add(mc);\t\t\t\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn devices;\n\t}\n\t\n\t// Signal to the main program to change its audio mixer\n\tprivate void changeMixer(String mixerName){\n\t\tif (theApp.changeMixer(mixerName)==false)\t{\n\t\t\tJOptionPane.showMessageDialog(null,\"Error changing mixer\\n\"+theApp.lineInThread.getMixerErrorMessage(),\"DMRDecode\",JOptionPane.ERROR_MESSAGE);\n\t\t}\n\t}\t\n\t\n}"
  },
  {
    "path": "src/main/java/com/dmr/DisplayModel.java",
    "content": "// Please note much of the code in this program was taken from the DSD software\n// and converted into Java. The author of this software is unknown but has the\n// GPG Key ID below\n\n// Copyright (C) 2010 DSD Author\n// GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6  F630 FAA2 635D 3F1D 7FD0)\n// \n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND ISC DISCLAIMS ALL WARRANTIES WITH\n// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,\n// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE\n// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n// PERFORMANCE OF THIS SOFTWARE.\n//\n\npackage com.dmr;\n\nimport java.util.Observable;\n\npublic class DisplayModel extends Observable {\n\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/DisplayView.java",
    "content": "// Please note much of the code in this program was taken from the DSD software\n// and converted into Java. The author of this software is unknown but has the\n// GPG Key ID below\n\n// Copyright (C) 2010 DSD Author\n// GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6  F630 FAA2 635D 3F1D 7FD0)\n// \n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND ISC DISCLAIMS ALL WARRANTIES WITH\n// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,\n// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE\n// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n// PERFORMANCE OF THIS SOFTWARE.\n//\n\npackage com.dmr;\n\nimport javax.swing.JComponent;\nimport java.util.Observer;\nimport java.util.Observable;\nimport java.awt.Color;\nimport java.awt.Font;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\n\npublic class DisplayView extends JComponent implements Observer  {\t\n\tpublic static final long serialVersionUID=1;\n\tprivate static final int DISPLAYCOUNT=150;\n\tprivate String display_string[]=new String[DISPLAYCOUNT];\n\tprivate Color displayColour[]=new Color[DISPLAYCOUNT];\n\tprivate Font displayFont[]=new Font[DISPLAYCOUNT];\n\tprivate int displayCounter=0;\n\tprivate DMRDecode theApp;\t\n\t\n\tpublic DisplayView (DMRDecode theApp) {\n\t\tthis.theApp=theApp;\t\n    }\n\t\t\t\n\tpublic void update (Observable o,Object rectangle)\t{\t\t\t\n\t}\n\t\t\t\n\t// Draw the main screen //\n\tpublic void paint (Graphics g) {\n\t\tint count=0,pos=20,i;\n\t\tif (displayCounter>0) i=displayCounter-1;\n\t\telse i=DISPLAYCOUNT-1;\n\t\tGraphics2D g2D=(Graphics2D)g;\t\n\t\t// Draw in the lines on the screen\n\t\t// taking account of the fact that the data is stored in a circular buffer\n\t\t// we need to display the oldest line stored first and then go backwards from\n\t\t// that point onwards\n\t\twhile(count<DISPLAYCOUNT)\t{\n\t\t\t// Only display info if something is stored in the display string\n\t\t\tif (display_string[i]!=null)\t{\n\t\t\t\tg.setColor(displayColour[i]);\n\t\t\t\tg.setFont(displayFont[i]);\n\t\t\t\tg2D.drawString(display_string[i],(5-theApp.horizontal_scrollbar_value),(pos-theApp.vertical_scrollbar_value));\t\n\t\t\t\tpos=pos+20;\n\t\t\t}\t\n\t\t\tif (i==0) i=DISPLAYCOUNT;\n\t\t\ti--;\n\t\t\tcount++;\n\t\t}\n\t}\n\t\n\t// Add a line to the display circular buffer //\n\tpublic void add_line (String line,Color tcol,Font tfont) {\n\t\tdisplay_string[displayCounter]=line;\n\t\tdisplayColour[displayCounter]=tcol;\n\t\tdisplayFont[displayCounter]=tfont;\n\t\t// Increment the circular buffer\n\t\tdisplayCounter++;\n\t\t// Check it hasn't reached its maximum size\n\t\tif (displayCounter==DISPLAYCOUNT) displayCounter=0;\n\t\trepaint();\n\t}\n\t\n\t// Clear the display screen\n\tpublic void clearScreen\t()\t{\n\t\tint a;\n\t\tdisplayCounter=0;\n\t\tfor (a=0;a<DISPLAYCOUNT;a++)\t{\n\t\t\tdisplay_string[a]=null;\n\t\t}\n\t\trepaint();\n\t}\n\t\n\t// Gets all the text on the screen and returns it as a string\n\tpublic String getText()\t{\n\t\tStringBuilder buffer=new StringBuilder();\n\t\tint i=displayCounter,count=0;\n\t\twhile(count<DISPLAYCOUNT)\t{\n\t\t\tif (display_string[i]!=null)\t{\n\t\t\t\tbuffer.append(display_string[i]);\n\t\t\t\tbuffer.append(\"\\n\");\n\t\t\t}\t\n\t\t\ti++;\n\t\t\tif (i>=DISPLAYCOUNT) i=0;\n\t\t\tcount++;\n\t\t}\n\t\treturn buffer.toString();\n\t}\t\n\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/EmbeddedLC.java",
    "content": "package com.dmr;\n\npublic class EmbeddedLC {\n\tprivate boolean rawLC[]=new boolean[128];\n\tprivate int currentState=-1;\n\tprivate boolean dataReady=false;\n\tprivate String lines[]=new String[3];\n\tprivate boolean lcData[]=new boolean[72];\n\t\n\t// Add LC data (which may consist of 4 blocks) to the data store\n\tpublic void addData (byte[] dibit_buf,int type)\t{\n\t\tint a,r=0;\n\t\tboolean rawdata[]=new boolean[32];\n\t\t// Convert from dibits into boolean\n\t\tfor (a=70;a<86;a++)\t{\n\t\t\tif (dibit_buf[a]==0)\t{\n\t\t\t\trawdata[r]=false;\n\t\t\t\trawdata[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==1)\t{\n\t\t\t\trawdata[r]=false;\n\t\t\t\trawdata[r+1]=true;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==2)\t{\n\t\t\t\trawdata[r]=true;\n\t\t\t\trawdata[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==3)\t{\n\t\t\t\trawdata[r]=true;\n\t\t\t\trawdata[r+1]=true;\n\t\t\t}\n\t\t\tr=r+2;\n\t\t}\n\t\t// Is this the first block of a 4 block embedded LC ?\n\t\tif (type==1)\t{\n\t\t\tfor (a=0;a<32;a++)\t{\n\t\t\t\trawLC[a]=rawdata[a];\n\t\t\t}\n\t\t\t// Show we are ready for the next LC block\n\t\t\tcurrentState=0;\n\t\t}\n\t\t// Is this the 2nd block of a 4 block embedded LC ?\n\t\telse if ((type==3)&&(currentState==0))\t{\n\t\t\tfor (a=0;a<32;a++)\t{\n\t\t\t\trawLC[a+32]=rawdata[a];\n\t\t\t}\n\t\t\t// Show we are ready for the next LC block\n\t\t\tcurrentState=1;\n\t\t}\n\t\t// Is this the 3rd block of a 4 block embedded LC ?\n\t\telse if ((type==3)&&(currentState==1))\t{\n\t\t\tfor (a=0;a<32;a++)\t{\n\t\t\t\trawLC[a+64]=rawdata[a];\n\t\t\t}\n\t\t\t// Show we are ready for the final LC block\n\t\t\tcurrentState=2;\n\t\t}\n\t\t// Is this the final block of a 4 block embedded LC ?\n\t\telse if ((type==2)&&(currentState==2))\t{\n\t\t\tfor (a=0;a<32;a++)\t{\n\t\t\t\trawLC[a+96]=rawdata[a];\n\t\t\t}\n\t\t\t// Process the complete data block\n\t\t\tif (processMultiBlockEmbeddedLC()==false)\t{\n\t\t\t\tdataReady=true;\n\t\t\t\tlines[0]=\"Bad Embedded Multi Block LC\";\n\t\t\t}\n\t\t}\n\t\t// Is this a single block embedded LC\n\t\telse if (type==0) processSingleBlockEmbeddedLC(rawdata);\n\t}\n\t\n\t// Unpack and error check a embedded LC\n\tprivate boolean processMultiBlockEmbeddedLC()\t{\n\t\tint a,b=0,crc;\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tboolean data[]=new boolean[128];\n\t\tboolean row[]=new boolean[16];\n\t\tsb.append(\"Embedded Multi Block LC : \");\n\t\t// The data is unpacked downwards in columns\n\t\tfor (a=0;a<128;a++)\t{\n\t\t\tdata[b]=rawLC[a];\n\t\t\tb=b+16;\n\t\t\tif (b>112) b=b-112;\n\t\t}\n\t\t// Hamming (16,11,4) check each row except the last one\n\t\tfor (a=0;a<=96;a=a+16)\t{\n\t\t\tfor (b=0;b<16;b++)\t{\n\t\t\t\trow[b]=data[a+b];\n\t\t\t}\n\t\t\tif (hamming16114(row)==false) return false;\n\t\t}\n\t\t// We have passed the Hamming check so extract the actual payload\n\t\tb=0;\n\t\tfor (a=0;a<11;a++)\t{\n\t\t\tlcData[b]=data[a];\n\t\t\tb++;\n\t\t}\n\t\tfor (a=16;a<27;a++)\t{\n\t\t\tlcData[b]=data[a];\n\t\t\tb++;\n\t\t}\n\t\tfor (a=32;a<42;a++)\t{\n\t\t\tlcData[b]=data[a];\n\t\t\tb++;\n\t\t}\n\t\tfor (a=48;a<58;a++)\t{\n\t\t\tlcData[b]=data[a];\n\t\t\tb++;\n\t\t}\n\t\tfor (a=64;a<74;a++)\t{\n\t\t\tlcData[b]=data[a];\n\t\t\tb++;\n\t\t}\n\t\tfor (a=80;a<90;a++)\t{\n\t\t\tlcData[b]=data[a];\n\t\t\tb++;\n\t\t}\n\t\tfor (a=96;a<106;a++)\t{\n\t\t\tlcData[b]=data[a];\n\t\t\tb++;\n\t\t}\n\t\t// Extract the 5 bit CRC\n\t\tif (data[42]==true) crc=16;\n\t\telse crc=0;\n\t\tif (data[58]==true) crc=crc+8;\n\t\tif (data[74]==true) crc=crc+4;\n\t\tif (data[90]==true) crc=crc+2;\n\t\tif (data[106]==true) crc++;\n\t\t// Now CRC check this\n\t\tcrc tCRC=new crc();\n\t\tif (tCRC.crcFiveBit(lcData,crc)==false) return false;\n\t\t// Display what we have in binary form\n\t\tfor (a=0;a<72;a++)\t{\n\t\t\tif (lcData[a]==false) sb.append(\"0\");\n\t\t\telse sb.append(\"1\");\n\t\t}\n\t\t// Convert from StringBuilder to a String\n\t\tlines[0]=sb.toString();\n\t\tdataReady=true;\n\t\treturn true;\n\t}\n\t\n\t// A Hamming (16,11,4) Check\n\tprivate boolean hamming16114 (boolean d[])\t{\n\t\tboolean c[]=new boolean[5];\n\t\t// Calculate the checksum this column should have\n\t\tc[0]=d[0]^d[1]^d[2]^d[3]^d[5]^d[7]^d[8];\n\t\tc[1]=d[1]^d[2]^d[3]^d[4]^d[6]^d[8]^d[9];\n\t\tc[2]=d[2]^d[3]^d[4]^d[5]^d[7]^d[9]^d[10];\n\t\tc[3]=d[0]^d[1]^d[2]^d[4]^d[6]^d[7]^d[10];\n\t\tc[4]=d[0]^d[2]^d[5]^d[6]^d[8]^d[9]^d[10];\n\t\t// Compare these with the actual bits\n\t\tif ((c[0]==d[11])&&(c[1]==d[12])&&(c[2]==d[13])&&(c[3]==d[14])&&(c[4]==d[15])) return true;\n\t\telse return false;\n\t}\n\t\n\t// Deal with a single block embedded LC\n\tprivate void processSingleBlockEmbeddedLC (boolean data[])\t{\n\t\tboolean isnull=true;\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tStringBuilder bin=new StringBuilder(250);\n\t\tint a;\n\t\tsb.append(\"Embedded Single Block LC : \");\n\t\t// Check if this message is all 0's as if it is then it is a null\n\t\tfor (a=0;a<32;a++)\t{\n\t\t\tif (data[a]==true)\t{\n\t\t\t\tbin.append(\"1\");\n\t\t\t\tisnull=false;\n\t\t\t}\n\t\t\telse \t{\n\t\t\t\tbin.append(\"0\");\n\t\t\t}\n\t\t}\n\t\t// Is this message a null short LC\n\t\tif (isnull==true)\t{\n\t\t\tsb.append(\"Null\");\n\t\t}\n\t\telse\t{\n\t\t\tsb.append(bin);\n\t\t}\n\t\tlines[0]=sb.toString();\n\t\tdataReady=true;\n\t}\n\t\n\t// Tell the main program if we have data to return\n\tpublic boolean getDataReady\t()\t{\n\t\treturn dataReady;\n\t}\n\t\n\t// Return the display lines to the main program\n\tpublic String[] getLines ()\t{\n\t\t// Make a copy of the objects lines\n\t\tString clines[]=new String[3];\n\t\tclines[0]=lines[0];\n\t\tclines[1]=lines[1];\n\t\tclines[2]=lines[2];\n\t\t// Clear all the lines\n\t\tlines[0]=null;\n\t\tlines[1]=null;\n\t\tlines[2]=null;\n\t\t// Clear the dataReady boolean so the program doesn't think there is more data\n\t\tdataReady=false;\n\t\t// Prepare for more data\n\t\tcurrentState=-1;\n\t\t// Return the copy of the lines\n\t\treturn clines;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/FullLinkControl.java",
    "content": "package com.dmr;\n\npublic class FullLinkControl {\n\tboolean pf;\n\tprivate String display[]=new String[3];\n\t\n\t// The main decode method\n\tpublic String[] decode (DMRDecode theApp,boolean bits[]) \t{\n\t\tint flco,fid,service;\n\t\t// PF\n\t\tpf=bits[0];\n\t\t// Bit 1 is reserved\n\t\tpf=bits[1];\n\t\t// FLCO\n\t\tif (bits[2]==true) flco=32;\n\t\telse flco=0;\n\t\tif (bits[3]==true) flco=flco+16;\n\t\tif (bits[4]==true) flco=flco+8;\n\t\tif (bits[5]==true) flco=flco+4;\n\t\tif (bits[6]==true) flco=flco+2;\n\t\tif (bits[7]==true) flco++;\n\t\t// FID\n\t\tif (bits[8]==true) fid=128;\n\t\telse fid=0;\n\t\tif (bits[9]==true) fid=fid+64;\n\t\tif (bits[10]==true) fid=fid+32;\n\t\tif (bits[11]==true) fid=fid+16;\n\t\tif (bits[12]==true) fid=fid+8;\n\t\tif (bits[13]==true) fid=fid+4;\n\t\tif (bits[14]==true) fid=fid+2;\n\t\tif (bits[15]==true) fid++;\n\t\t// Service options\n\t\tif (bits[16]==true) service=128;\n\t\telse service=0;\n\t\tif (bits[17]==true) service=service+64;\n\t\tif (bits[18]==true) service=service+32;\n\t\tif (bits[19]==true) service=service+16;\n\t\tif (bits[20]==true) service=service+8;\n\t\tif (bits[21]==true) service=service+4;\n\t\tif (bits[22]==true) service=service+2;\n\t\tif (bits[23]==true) service++;\n\t\t// PDU types \n\t\tif (flco==0) group_v_ch_usr(theApp,bits);\n\t\telse if (flco==3) uu_v_ch_usr(theApp,bits);\n\t\telse if (flco==4) big_m_flco4(theApp,bits);\n\t\telse if (flco==48) td_lc(theApp,bits);\n\t\telse unknown_flc(flco,fid,bits);\n\t\treturn display;\n\t}\n\t\n\t// Group Voice Channer User LC\n\tvoid group_v_ch_usr (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tdisplay[0]=\"Group Voice Channel User LC\";\n\t\t// Service Options\n\t\tdisplay[1]=utils.decodeServiceOptions(bits,16);\n\t\t// Group address\n\t\tint group=utils.retAddress(bits,24);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,48);\n\t\tsb.append(\"Group Address : \"+Integer.toString(group));\n\t\tsb.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[2]=sb.toString();\n\t\t// Log these users\n\t\t// Group\n\t\tif (theApp.usersLogged.addUser(group)==true)\t{\n\t\t\tindex=theApp.usersLogged.findUserIndex(group);\n\t\t\tif (index!=-1)\t{\n\t\t\t\ttheApp.usersLogged.setAsGroup(index);\n\t\t\t\ttheApp.usersLogged.setChannel(index,theApp.currentChannel);\n\t\t\t}\n\t\t}\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t\tindex=theApp.usersLogged.findUserIndex(source);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsGroupUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,theApp.currentChannel);\n\t\t}\n\t\t// Display this in a label on the status bar\n\t\tStringBuilder lab=new StringBuilder(250);\n\t\tlab.append(\"Group Call to Group \");\n\t\tlab.append(Integer.toString(group));\n\t\tlab.append(\" from \");\n\t\tlab.append(Integer.toString(source));\n\t\tif (theApp.currentChannel==1) theApp.setCh1Label(lab.toString(),theApp.labelBusyColour);\n\t\telse theApp.setCh2Label(lab.toString(),theApp.labelBusyColour);\n\t\t// Quick log\n\t\tif (theApp.isQuickLog()==true) theApp.quickLogData(\"Group Voice Call to Group\",group,source,theApp.currentChannel,display[1]);\n\t}\n\t\n\t// Unit to Unit Voice Channel User LC\n\tvoid uu_v_ch_usr (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tdisplay[0]=\"Unit to Unit Voice Channel User LC\";\n\t\t// Service Options\n\t\tdisplay[1]=utils.decodeServiceOptions(bits,16);\n\t\t// Target address\n\t\tint target=utils.retAddress(bits,24);\n\t\t// Source address\n\t\tint source=utils.retAddress(bits,48);\n\t\tsb.append(\"Target Address : \"+Integer.toString(target));\n\t\tsb.append(\" Source Address : \"+Integer.toString(source));\n\t\tdisplay[2]=sb.toString();\n\t\t// Log these users\n\t\t// Target\n\t\ttheApp.usersLogged.addUser(target);\t\n\t\tindex=theApp.usersLogged.findUserIndex(target);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsUnitUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,theApp.currentChannel);\n\t\t}\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t\tindex=theApp.usersLogged.findUserIndex(source);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsUnitUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,theApp.currentChannel);\n\t\t}\n\t\t// Display this in a label on the status bar\n\t\tStringBuilder lab=new StringBuilder(250);\n\t\tlab.append(\"Unit to Unit Call from \");\n\t\tlab.append(Integer.toString(source));\n\t\tlab.append(\" to \");\n\t\tlab.append(Integer.toString(target));\n\t\tif (theApp.currentChannel==1) theApp.setCh1Label(lab.toString(),theApp.labelBusyColour);\n\t\telse theApp.setCh2Label(lab.toString(),theApp.labelBusyColour);\n\t\t// Quick log\n\t\tif (theApp.isQuickLog()==true) theApp.quickLogData(\"Unit to Unit Voice Call\",target,source,theApp.currentChannel,display[1]);\n\t}\n\t\n\t// Terminator Data Link Control PDU\n\tvoid td_lc (DMRDecode theApp,boolean bits[])\t{\n\t\tint index;\n\t\tUtilities utils=new Utilities();\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\tdisplay[0]=\"Terminator Data Link Control PDU\";\n\t\t// Destination LLID\n\t\tint dllid=utils.retAddress(bits,16);\n\t\t// Source LLID\n\t\tint sllid=utils.retAddress(bits,40);\n\t\tsb.append(\"Destination Logical Link ID : \"+Integer.toString(dllid));\n\t\tsb.append(\" Source Logical Link ID : \"+Integer.toString(sllid));\n\t\tdisplay[1]=sb.toString();\n\t\t// Log these users\n\t\t// Destination\n\t\ttheApp.usersLogged.addUser(dllid);\n\t\tindex=theApp.usersLogged.findUserIndex(dllid);\n\t\tif (index!=-1) theApp.usersLogged.setAsDataUser(index);\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(sllid);\n\t\tindex=theApp.usersLogged.findUserIndex(sllid);\n\t\tif (index!=-1) theApp.usersLogged.setAsDataUser(index);\n\t\t// Display this in a label on the status bar\n\t\tStringBuilder lab=new StringBuilder(250);\n\t\tlab.append(\"Data Call from \");\n\t\tlab.append(Integer.toString(sllid));\n\t\tlab.append(\" to \");\n\t\tlab.append(Integer.toString(dllid));\n\t\tif (theApp.currentChannel==1) theApp.setCh1Label(lab.toString(),theApp.labelBusyColour);\n\t\telse theApp.setCh2Label(lab.toString(),theApp.labelBusyColour);\n\t\t// Quick log\n\t\tif (theApp.isQuickLog()==true) theApp.quickLogData(\"Terminator Data Link Control PDU\",dllid,sllid,theApp.currentChannel,\"\");\n\t}\n\t\n\t// CP FLCO=4\n\tvoid big_m_flco4 (DMRDecode theApp,boolean bits[])\t{\n\t\tint group,source,a,lcn,index;\n\t\tStringBuilder sb1=new StringBuilder(300);\n\t\tStringBuilder sb2=new StringBuilder(300);\n\t\tdisplay[0]=\"Capacity Plus Full Link Control LC : FLCO=4\";\n        // Group\n\t\tif (bits[40]==true) group=127;\n\t\telse group=0;\n\t\tif (bits[41]==true) group=group+64;\n\t\tif (bits[42]==true) group=group+32;\n\t\tif (bits[43]==true) group=group+16;\n\t\tif (bits[44]==true) group=group+8;\n\t\tif (bits[45]==true) group=group+4;\n\t\tif (bits[46]==true) group=group+2;\n\t\tif (bits[47]==true) group++;\n\t\t// Bits 48 - 51 ??\n\t\t// LCN\n\t\tif (bits[52]==true) lcn=8;\n\t\telse lcn=0;\n\t\tif (bits[53]==true) lcn=lcn+4;\n\t\tif (bits[54]==true) lcn=lcn+2;\n\t\tif (bits[55]==true) lcn++;\n\t\t// Source\n\t\tif (bits[56]==true) source=32768;\n\t\telse source=0;\n\t\tif (bits[57]==true) source=source+16384;\n\t\tif (bits[58]==true) source=source+8192;\n\t\tif (bits[59]==true) source=source+4096;\n\t\tif (bits[60]==true) source=source+2048;\n\t\tif (bits[61]==true) source=source+1024;\n\t\tif (bits[62]==true) source=source+512;\n\t\tif (bits[63]==true) source=source+256;\n\t\tif (bits[64]==true) source=source+128;\n\t\tif (bits[65]==true) source=source+64;\n\t\tif (bits[66]==true) source=source+32;\n\t\tif (bits[67]==true) source=source+16;\n\t\tif (bits[68]==true) source=source+8;\n\t\tif (bits[69]==true) source=source+4;\n\t\tif (bits[70]==true) source=source+2;\n\t\tif (bits[71]==true) source++;\n \t\t// Make up the 2nd line\n\t\tsb1.append(\"Group Address \"+Integer.toString(group)+\" Source Address \"+Integer.toString(source)+\" LCN \"+Integer.toString(lcn));\n\t\tdisplay[1]=sb1.toString();\n\t\t// Log these users\n\t\t// Group\n\t\tif (theApp.usersLogged.addUser(group)==true)\t{\n\t\t\tindex=theApp.usersLogged.findUserIndex(group);\n\t\t\tif (index!=-1)\t{\n\t\t\t\ttheApp.usersLogged.setAsGroup(index);\n\t\t\t\ttheApp.usersLogged.setChannel(index,theApp.currentChannel);\n\t\t\t}\n\t\t}\n\t\t// Source\n\t\ttheApp.usersLogged.addUser(source);\n\t\tindex=theApp.usersLogged.findUserIndex(source);\n\t\tif (index!=-1)\t{\n\t\t\ttheApp.usersLogged.setAsGroupUser(index);\n\t\t\ttheApp.usersLogged.setChannel(index,theApp.currentChannel);\n\t\t}\n\t\t// Display the full binary on the bottom line if in debug mode\n\t\tif (theApp.isDebug()==true)\t{\n\t\t\tfor (a=16;a<72;a++)\t{\n\t\t\t\tif (bits[a]==true) sb2.append(\"1\");\n\t\t\t\telse sb2.append(\"0\");\n\t\t\t}\n\t\tdisplay[2]=sb2.toString();\n\t\t}\n\t\t// Display this in a label on the status bar\n\t\tStringBuilder lab=new StringBuilder(250);\n\t\tlab.append(\"CP Group Call to Group \");\n\t\tlab.append(Integer.toString(group));\n\t\tlab.append(\" from \");\n\t\tlab.append(Integer.toString(source));\n\t\tif (theApp.currentChannel==1) theApp.setCh1Label(lab.toString(),theApp.labelBusyColour);\n\t\telse theApp.setCh2Label(lab.toString(),theApp.labelBusyColour);\n\t\t// Quick log\n\t\tif (theApp.isQuickLog()==true) theApp.quickLogData(\"Capacity Plus Full Link Control LC\",group,source,theApp.currentChannel,\"\");\n\t}\n\t\n\t// Handle unknown Full Link Control types\n\tprivate void unknown_flc (int flco,int fid,boolean bits[])\t{\n\t\tint a;\n\t\tStringBuilder sb=new StringBuilder(300);\n\t    sb.append(\"Unknown Full Link Control LC : FLCO=\"+Integer.toString(flco)+\" + FID=\"+Integer.toString(fid)+\" \");\n\t\t// Display the binary\n\t\tfor (a=16;a<72;a++)\t{\n\t\t\tif (bits[a]==true) sb.append(\"1\");\n\t\t\telse sb.append(\"0\");\n\t\t}\n\t\tdisplay[0]=sb.toString();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/JStatusBar.java",
    "content": "package com.dmr;\n\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\n\nimport javax.swing.*;\nimport javax.swing.border.Border;\n\npublic class JStatusBar extends JPanel {\n\tpublic static final long serialVersionUID = 1;\n\tprivate JLabel logMode=new JLabel();\n\tprivate JLabel syncLabel=new JLabel();\n\tprivate JLabel ch1Label=new JLabel();\n\tprivate JLabel ch2Label=new JLabel();\n\tprivate JLabel colourCodeLabel=new JLabel();\n\tprivate JLabel systemLabel=new JLabel();\n\tprivate JProgressBar volumeBar=new JProgressBar(0,100);\n\tprivate Border loweredbevel=BorderFactory.createLoweredBevelBorder();\n\tprivate JButton pauseButton=new JButton(\"Pause\");\n\tprivate DMRDecode TtheApp;\n\t\n\tpublic JStatusBar() {\n\t\tlogMode.setHorizontalAlignment(SwingConstants.LEFT);\n\t\tlogMode.updateUI();\n\t\tlogMode.setBorder(loweredbevel);\n\t\tsyncLabel.setHorizontalAlignment(SwingConstants.LEFT);\n\t\tsyncLabel.setBorder(loweredbevel);\n\t\tsyncLabel.updateUI();\n\t\tch1Label.setHorizontalAlignment(SwingConstants.LEFT);\n\t\tch1Label.setBorder(loweredbevel);\n\t\tch1Label.updateUI();\n\t\tch2Label.setHorizontalAlignment(SwingConstants.LEFT);\n\t\tch2Label.setBorder(loweredbevel);\n\t\tch2Label.updateUI();\n\t\tcolourCodeLabel.setHorizontalAlignment(SwingConstants.LEFT);\n\t\tcolourCodeLabel.setBorder(loweredbevel);\n\t\tcolourCodeLabel.updateUI();\n\t\tsystemLabel.setHorizontalAlignment(SwingConstants.LEFT);\n\t\tsystemLabel.setBorder(loweredbevel);\n\t\tsystemLabel.updateUI();\n\t\tpauseButton.addActionListener(new ButtonListener());\n\t\t// Give the volume progress bar a border //\n\t\tvolumeBar.setBorder(loweredbevel);\n\t\t// Ensure the elements of the status bar are displayed from the left\n\t\tthis.setLayout(new FlowLayout(FlowLayout.LEFT));\n\t\tthis.add(pauseButton,BorderLayout.CENTER);\n\t\tthis.add(volumeBar,BorderLayout.CENTER);\n\t\tthis.add(syncLabel,BorderLayout.CENTER);\n\t\tthis.add(logMode,BorderLayout.CENTER);\n\t\tthis.add(colourCodeLabel,BorderLayout.CENTER);\n\t\tthis.add(ch1Label,BorderLayout.CENTER);\n\t\tthis.add(ch2Label,BorderLayout.CENTER);\n\t\tthis.add(systemLabel,BorderLayout.CENTER);\n\t}\n\t\n\t// Sets the logging label text\n\tpublic void setLoggingStatus(String text) {\n\t\tif (TtheApp!=null)\t{\n\t\t\tif (TtheApp.isPauseScreen()==true) return;\n\t\t}\n\t\tlogMode.setText(text);\n\t}\n\n\t// Sets the sync mode label\n\tpublic void setSyncLabel (boolean syn)\t{\n\t\tif (TtheApp!=null)\t{\n\t\t\tif (TtheApp.isPauseScreen()==true) return;\n\t\t}\n\t\t// Have sync\n\t\tif (syn==true)\t{\n\t\t\tsyncLabel.setText(\"SYNC\");\n\t\t\tsyncLabel.setForeground(Color.GREEN);\n\t\t}\n\t\telse\t{\n\t\t\tsyncLabel.setText(\"NO SYNC\");\n\t\t\tsyncLabel.setForeground(Color.RED);\n\t\t}\n\t}\n\t\n\t// Set the volume bar display\n\tpublic void setVolumeBar(int val) {\n\t\tif (TtheApp!=null)\t{\n\t\t\tif (TtheApp.isPauseScreen()==true) return;\n\t\t}\n\t\tif (val<40){\n\t\t\tvolumeBar.setForeground(Color.yellow);\n\t\t}else if((val>40)&&(val<70)){\n\t\t\tvolumeBar.setForeground(Color.green);\n\t\t}else {\n\t\t\tvolumeBar.setForeground(Color.red);\n\t\t\t//greater 100 reset vol bar to 100\n\t\t\tval=100;\n\t\t}\n\t\t// Set the class value //\n\t\tvolumeBar.setValue(val);\n\t}\n\t\n\tpublic void setCh1Label (String label,Color c)\t{\n\t\tif (TtheApp!=null)\t{\n\t\t\tif (TtheApp.isPauseScreen()==true) return;\n\t\t}\n\t\tlabel=\"Ch 1 : \"+label;\n\t\tch1Label.setText(label);\n\t\tch1Label.setForeground(c);\n\t}\n\t\n\tpublic void setCh2Label (String label,Color c)\t{\n\t\tif (TtheApp!=null)\t{\n\t\t\tif (TtheApp.isPauseScreen()==true) return;\n\t\t}\n\t\tlabel=\"Ch 2 : \"+label;\n\t\tch2Label.setText(label);\n\t\tch2Label.setForeground(c);\n\t}\n\t\n\tpublic void setColourCodeLabel (int cc,Color col)\t{\n\t\tif (TtheApp!=null)\t{\n\t\t\tif (TtheApp.isPauseScreen()==true) return;\n\t\t}\n\t\tString label;\n\t\tif (cc==-1) label=\"Colour Code : Unknown\";\n\t\telse label=\"Colour Code : \"+Integer.toString(cc);\n\t\tcolourCodeLabel.setText(label);\n\t\tcolourCodeLabel.setForeground(col);\n\t}\n\t\n\tpublic void setSystemLabel (String text,Color col)\t{\n\t\tsystemLabel.setText(text);\n\t\tsystemLabel.setForeground(col);\n\t}\n\t\n\tpublic void setApp (DMRDecode theApp)\t{\n\t\tTtheApp=theApp;\n\t}\n\n\t// This class listens for button events\n\tclass ButtonListener implements ActionListener {\n\t\t  ButtonListener() {\n\t\t  }\n\n\t\t  public void actionPerformed(ActionEvent e) {\n\t\t\t// The user wants to pause the display\n\t\t\tif (e.getActionCommand().equals(\"Pause\")) {\n\t\t    \tpauseButton.setText(\"Restart\");\n\t\t    \tif (TtheApp!=null) TtheApp.setPauseScreen(true);\n\t\t    }\n\t\t    // The user wants to restart the display\n\t\t    if (e.getActionCommand().equals(\"Restart\")) {\n\t\t    \tpauseButton.setText(\"Pause\");\n\t\t\t    if (TtheApp!=null) TtheApp.setPauseScreen(false);\n\t\t\t    }\n\t\t  }\n\t\t}\n\t\n}\n\n\n"
  },
  {
    "path": "src/main/java/com/dmr/ShortLC.java",
    "content": "package com.dmr;\n\npublic class ShortLC {\n\tprivate boolean dataReady;\n\tprivate String line;\n\tprivate boolean rawData[]=new boolean[69];\n\tprivate boolean crcResult=false;\n\tprivate int currentState=-1;\n\tprivate DMRDecode TtheApp;\n\t\n\t// Add data to the Short LC data buffer\n\t// Type 0 if First fragment of LC\n\t// Type 1 if Continuation fragment of LC\n\t// Type 2 if Last fragment of LC\n\tpublic void addData (boolean[] CACHbuf,int type)\t{\n\t\tint a,b=7,rawCounter=0;\n\t\tdataReady=false;\n\t\t// First fragment ?\n\t\t// If so reset the the counters \n\t\tif (type==0)\t{\n\t\t\trawCounter=0;\n\t\t\t// Ensure nothing else has arrived before\n\t\t\tif (currentState!=-1) return;\n\t\t\t// Set the current state to 0 to indicate a first fragment has arrived\n\t\t\tcurrentState=0;\n\t\t\t// Clear the display line\n\t\t\tline=\"\";\n\t\t}\n\t\t// Continuation fragments\n\t\telse if (type==1)\t{\n\t\t\tif (currentState==0) {\n\t\t\t\trawCounter=17;\n\t\t\t\tcurrentState=1;\n\t\t\t}\n\t\t\telse if (currentState==1)\t{\n\t\t\t\trawCounter=34;\n\t\t\t\tcurrentState=2;\n\t\t\t}\n\t\t\telse if (currentState==-1)\t{\n\t\t\t\trawCounter=0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// Last fragment\n\t\telse if (type==2)\t{\n\t\t\t// Ensure that a first fragment and two continuation fragments have arrived\n\t\t\tif (currentState!=2)\t{\n\t\t\t\tcurrentState=-1;\n\t\t\t\trawCounter=0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse rawCounter=51;\n\t\t}\n\t\t// Add the data\n\t\tfor (a=rawCounter;a<(rawCounter+17);a++)\t{\n\t\t\t// Ignore the TACT\n\t\t\trawData[a]=CACHbuf[b];\n\t\t\tb++;\n\t\t}\n\t\t// Has the fragment ended ?\n\t\tif (type==2)\t{\n\t\t\tdecode();\n\t\t\tcurrentState=-1;\n\t\t\trawCounter=0;\n\t\t}\n\t}\n\t\n\t// Make the text string available\n\tpublic String getLine()\t{\n\t\treturn line;\n\t}\n\t\n\t// Tell the main object if decoded data is available\n\tpublic boolean isDataReady()\t{\n\t\treturn dataReady;\n\t}\n\t\n\t// Tell the main object if the CRC and Hamming checks are OK\n\tpublic boolean isCRCgood()\t{\n\t\treturn crcResult;\n\t}\n\t\n\t// Clear the data ready boolean\n\tpublic void clrDataReady()\t{\n\t\tdataReady=false;\n\t}\n\t\n\t// Deinterleave and error check the short LC\n\tpublic void decode()\t{\n\t\tcrcResult=false;\n\t\tif (shortLCHamming(rawData)==true)\t{\n\t\t\tboolean shortLC[]=deInterleaveShortLC(rawData);\n\t\t\tif (shortLCcrc(shortLC)==true)\t{\n\t\t\t\tline=decodeShortLC(shortLC);\n\t\t\t\tcrcResult=true;\n\t\t\t}\n\t\t}\n\t\telse line=\"\";\n\t\tdataReady=true;\n\t}\n\t\n\t// Deinterleave a Short LC from 4 CACH bursts\n\tprivate boolean[] deInterleaveShortLC (boolean raw[])\t{\n\t\tint a,pos;\n\t\tfinal int sequence[]={\n\t\t\t\t0,4,8,12,16,20,24,28,32,36,40,44,\n\t\t\t\t1,5,9,13,17,21,25,29,33,37,41,45,\n\t\t\t\t2,6,10,14,18,22,26,30,34,38,42,46};\n\t\tboolean[] deinter=new boolean[36];\n\t\tfor (a=0;a<36;a++)\t{\n\t\t\tpos=sequence[a];\n\t\t\tdeinter[a]=raw[pos];\n\t\t}\n\t\treturn deinter;\n\t}\n\t\n\t// Hamming check 3 of the 4 CACH rows\n\tprivate boolean shortLCHamming (boolean raw[])\t{\n\t\tint a,pos;\n\t\tfinal int sequence1[]={0,4,8,12,16,20,24,28,32,36,40,44};\n\t\tfinal int sequence2[]={1,5,9,13,17,21,25,29,33,37,41,45};\n\t\tfinal int sequence3[]={2,6,10,14,18,22,26,30,34,38,42,46};\n\t\tfinal int ham1[]={48,52,56,60,64};\n\t\tfinal int ham2[]={49,53,57,61,65};\n\t\tfinal int ham3[]={50,54,58,62,66};\n\t\tboolean[] d=new boolean[12];\n\t\tboolean[] p=new boolean[5];\n\t\tboolean[] c=new boolean[5];\n\t\t// Row 1\n\t\tfor (a=0;a<12;a++)\t{\n\t\t\tpos=sequence1[a];\n\t\t\td[a]=raw[pos];\n\t\t\tif (a<5)\t{\n\t\t\t\tpos=ham1[a];\n\t\t\t\tp[a]=raw[pos];\n\t\t\t}\n\t\t}\n\t\tc[0]=d[0]^d[1]^d[2]^d[3]^d[6]^d[7]^d[9];\n\t\tc[1]=d[0]^d[1]^d[2]^d[3]^d[4]^d[7]^d[8]^d[10];\n\t\tc[2]=d[1]^d[2]^d[3]^d[4]^d[5]^d[8]^d[9]^d[11];\n\t\tc[3]=d[0]^d[1]^d[4]^d[5]^d[7]^d[10];\n\t\tc[4]=d[0]^d[1]^d[2]^d[5]^d[6]^d[8]^d[11];\n\t\tfor (a=0;a<5;a++)\t{\n\t\t\tif (c[a]!=p[a]) return false;\n\t\t}\n\t\t// Row 2\n\t\tfor (a=0;a<12;a++)\t{\n\t\t\tpos=sequence2[a];\n\t\t\td[a]=raw[pos];\n\t\t\tif (a<5)\t{\n\t\t\t\tpos=ham2[a];\n\t\t\t\tp[a]=raw[pos];\n\t\t\t}\n\t\t}\n\t\tc[0]=d[0]^d[1]^d[2]^d[3]^d[6]^d[7]^d[9];\n\t\tc[1]=d[0]^d[1]^d[2]^d[3]^d[4]^d[7]^d[8]^d[10];\n\t\tc[2]=d[1]^d[2]^d[3]^d[4]^d[5]^d[8]^d[9]^d[11];\n\t\tc[3]=d[0]^d[1]^d[4]^d[5]^d[7]^d[10];\n\t\tc[4]=d[0]^d[1]^d[2]^d[5]^d[6]^d[8]^d[11];\n\t\tfor (a=0;a<5;a++)\t{\n\t\t\tif (c[a]!=p[a]) return false;\n\t\t}\n\t\t// Row 3\n\t\tfor (a=0;a<12;a++)\t{\n\t\t\tpos=sequence3[a];\n\t\t\td[a]=raw[pos];\n\t\t\tif (a<5)\t{\n\t\t\t\tpos=ham3[a];\n\t\t\t\tp[a]=raw[pos];\n\t\t\t}\n\t\t}\n\t\tc[0]=d[0]^d[1]^d[2]^d[3]^d[6]^d[7]^d[9];\n\t\tc[1]=d[0]^d[1]^d[2]^d[3]^d[4]^d[7]^d[8]^d[10];\n\t\tc[2]=d[1]^d[2]^d[3]^d[4]^d[5]^d[8]^d[9]^d[11];\n\t\tc[3]=d[0]^d[1]^d[4]^d[5]^d[7]^d[10];\n\t\tc[4]=d[0]^d[1]^d[2]^d[5]^d[6]^d[8]^d[11];\n\t\tfor (a=0;a<5;a++)\t{\n\t\t\tif (c[a]!=p[a]) return false;\n\t\t}\n\t\t// All done so must have passed\n\t\treturn true;\n\t}\n\t\n\t// Test if the short LC passes its CRC8 test\n\tprivate boolean shortLCcrc (boolean dataBits[])\t{\n\t\tint a;\n\t\tcrc tCRC=new crc();\n\t\ttCRC.setCrc8Value(0);\n\t\tfor (a=0;a<dataBits.length;a++)\t{\n\t\t\ttCRC.crc8(dataBits[a]);\n\t\t}\n\t\tif (tCRC.getCrc8Value()==0) return true;\n\t\telse return false;\n\t}\n\t\n\t// Decode and display the info in SHORT LC PDUs\n\tprivate String decodeShortLC (boolean db[])\t{\n\t\tint slco,a;\n\t\tStringBuilder dline=new StringBuilder(250);\n\t\t// Calculate the SLCO\n\t\tif (db[0]==true) slco=8;\n\t\telse slco=0;\n\t\tif (db[1]==true) slco=slco+4;\n\t\tif (db[2]==true) slco=slco+2;\n\t\tif (db[3]==true) slco++;\n\t\t// Short LC Types\n\t\tif (slco==0)\t{\n\t\t\tdline.append(\"Nul_Msg\");\n\t\t}\n\t\telse if (slco==1)\t{\n\t\t\tint addr1,addr2,inf;\n\t\t\tdline.append(\"Act_Updt - \");\n\t\t\t// Slot 1\n\t\t\tif (db[4]==true) inf=8;\n\t\t\telse inf=0;\n\t\t\tif (db[5]==true) inf=inf+4;\n\t\t\tif (db[6]==true) inf=inf+2;\n\t\t\tif (db[7]==true) inf++;\n\t\t\tdline.append(decodeAct_Updt(inf,1));\n\t\t\t// Hashed Address\n\t\t\tif (inf!=0)\t{\n\t\t\t\tif (db[12]==true) addr1=128;\n\t\t\t\telse addr1=0;\n\t\t\t\tif (db[13]==true) addr1=addr1+64;\n\t\t\t\tif (db[14]==true) addr1=addr1+32;\n\t\t\t\tif (db[15]==true) addr1=addr1+16;\n\t\t\t\tif (db[16]==true) addr1=addr1+8;\n\t\t\t\tif (db[17]==true) addr1=addr1+4;\n\t\t\t\tif (db[18]==true) addr1=addr1+2;\n\t\t\t\tif (db[19]==true) addr1++;\n\t\t\t\tdline.append(\" Hashed Addr \"+Integer.toString(addr1));\n\t\t\t}\n\t\t\tdline.append(\" : \");\n\t\t\t// Slot 2\n\t\t\tif (db[8]==true) inf=8;\n\t\t\telse inf=0;\n\t\t\tif (db[9]==true) inf=inf+4;\n\t\t\tif (db[10]==true) inf=inf+2;\n\t\t\tif (db[11]==true) inf++;\n\t\t\tdline.append(decodeAct_Updt(inf,2));\n\t\t\tif (inf!=0)\t{\n\t\t\t\t// Hashed Address\n\t\t\t\tif (db[20]==true) addr2=128;\n\t\t\t\telse addr2=0;\n\t\t\t\tif (db[21]==true) addr2=addr2+64;\n\t\t\t\tif (db[22]==true) addr2=addr2+32;\n\t\t\t\tif (db[23]==true) addr2=addr2+16;\n\t\t\t\tif (db[24]==true) addr2=addr2+8;\n\t\t\t\tif (db[25]==true) addr2=addr2+4;\n\t\t\t\tif (db[26]==true) addr2=addr2+2;\n\t\t\t\tif (db[27]==true) addr2++;\n\t\t\t\tdline.append(\" Hashed Addr \"+Integer.toString(addr2));\n\t\t\t}\n\t\t}\n\t\t// Tier III SYS_Parms\n\t\telse if (slco==2)\t{\n\t\t\tint model,net=0,site=0,par;\n\t\t\t// Start to make up the display\n\t\t\tdline.append(\"SYS_Parms : \");\n\t\t\t// Model\n\t\t\tif (db[4]==true) model=2;\n\t\t\telse model=0;\n\t\t\tif (db[5]==true) model++;\n\t\t\t// Par\n\t\t\tif (db[18]==true) par=2;\n\t\t\telse par=0;\n\t\t\tif (db[19]==true) par++;\n\t\t\t// Tiny\n\t\t\tif (model==0)\t{\n\t\t\t\tdline.append(\"Tiny Network\");\n\t\t\t\t// Net\n\t\t\t\tif (db[6]==true) net=256;\n\t\t\t\tif (db[7]==true) net=net+128;\n\t\t\t\tif (db[8]==true) net=net+64;\n\t\t\t\tif (db[9]==true) net=net+32;\n\t\t\t\tif (db[10]==true) net=net+16;\n\t\t\t\tif (db[11]==true) net=net+8;\n\t\t\t\tif (db[12]==true) net=net+4;\n\t\t\t\tif (db[13]==true) net=net+2;\n\t\t\t\tif (db[14]==true) net++;\n\t\t\t\t// Site\n\t\t\t\tif (db[15]==true) site=4;\n\t\t\t\tif (db[16]==true) site=site+2;\n\t\t\t\tif (db[17]==true) site++;\t\t\t\t\n\t\t\t}\n\t\t\t// Small\n\t\t\telse if (model==1)\t{\n\t\t\t\tdline.append(\"Small Network\");\n\t\t\t\t// Net\n\t\t\t\tif (db[6]==true) net=64;\n\t\t\t\tif (db[7]==true) net=net+32;\n\t\t\t\tif (db[8]==true) net=net+16;\n\t\t\t\tif (db[9]==true) net=net+8;\n\t\t\t\tif (db[10]==true) net=net+4;\n\t\t\t\tif (db[11]==true) net=net+2;\n\t\t\t\tif (db[12]==true) net++;\n\t\t\t\t// Site\n\t\t\t\tif (db[13]==true) site=16;\n\t\t\t\tif (db[14]==true) site=site+8;\n\t\t\t\tif (db[15]==true) site=site+4;\n\t\t\t\tif (db[16]==true) site=site+2;\n\t\t\t\tif (db[17]==true) site++;\n\t\t\t}\n\t\t\t// Large\n\t\t\telse if (model==2)\t{\n\t\t\t\tdline.append(\"Large Network\");\n\t\t\t\t// Net\n\t\t\t\tif (db[6]==true) net=8;\n\t\t\t\tif (db[7]==true) net=net+4;\n\t\t\t\tif (db[8]==true) net=net+2;\n\t\t\t\tif (db[9]==true) net++;\n\t\t\t\t// Site\n\t\t\t\tif (db[10]==true) site=128;\n\t\t\t\tif (db[11]==true) site=site+64;\n\t\t\t\tif (db[12]==true) site=site+32;\n\t\t\t\tif (db[13]==true) site=site+16;\n\t\t\t\tif (db[14]==true) site=site+8;\n\t\t\t\tif (db[15]==true) site=site+4;\n\t\t\t\tif (db[16]==true) site=site+2;\n\t\t\t\tif (db[17]==true) site++;\n\t\t\t}\n\t\t\t// Huge\n\t\t\telse if (model==3)\t{\n\t\t\t\tdline.append(\"Huge Network\");\n\t\t\t\t// Net\n\t\t\t\tif (db[6]==true) net=2;\n\t\t\t\tif (db[7]==true) net++;\n\t\t\t\t// Site\n\t\t\t\tif (db[8]==true) site=512;\n\t\t\t\tif (db[9]==true) site=site+256;\n\t\t\t\tif (db[10]==true) site=site+128;\n\t\t\t\tif (db[11]==true) site=site+64;\n\t\t\t\tif (db[12]==true) site=site+32;\n\t\t\t\tif (db[13]==true) site=site+16;\n\t\t\t\tif (db[14]==true) site=site+8;\n\t\t\t\tif (db[15]==true) site=site+4;\n\t\t\t\tif (db[16]==true) site=site+2;\n\t\t\t\tif (db[17]==true) site++;\n\t\t\t}\n\t\t\tdline.append(\" NET=\"+Integer.toString(net)+\" SITE=\"+Integer.toString(site));\n\t\t\t// PAR\n\t\t\tif (par==1) dline.append(\" Category A MSs only permitted\");\n\t\t\telse if (par==2) dline.append(\" Category B MSs only permitted\");\n\t\t\telse if (par==3) dline.append(\" Category A MSs and B MSs permitted\");\n\t\t\t// Reg\n\t\t\tboolean reg=db[20];\n\t\t\tint counter=0;\n\t\t\t// Counter\n\t\t\tif (db[21]==true) counter=256;\n\t\t\tif (db[22]==true) counter=counter+128;\n\t\t\tif (db[23]==true) counter=counter+64;\n\t\t\tif (db[24]==true) counter=counter+32;\n\t\t\tif (db[25]==true) counter=counter+16;\n\t\t\tif (db[26]==true) counter=counter+8;\n\t\t\tif (db[27]==true) counter=counter+4;\n\t\t\tif (db[28]==true) counter=counter+2;\n\t\t\tif (db[29]==true) counter++;\n\t\t\tdline.append(\" Common_Slot_Counter=\"+Integer.toString(counter));\n\t\t\tif (reg==true) dline.append(\" Reg=1\");\n\t\t\telse dline.append(\" Reg=0\");\n\t\t\t// Display the network/site in the system label\n\t\t\tString ldip=Integer.toString(net)+\"/\"+Integer.toString(site);\n\t\t\tTtheApp.setSystemLabel(ldip);\n\t\t}\n\t\t\n\t\t// Connect Plus SLCO 9\n\t\telse if (slco==9)\t{\n\t\t\tint netID,siteID;\n\t\t\tdline.append(\"Connect Plus Voice Channel SLCO=\"+Integer.toString(slco)+\" Network: \");\n\t\t\t// Network ID\n\t\t\tif (db[4]==true) netID=2048;\n\t\t\telse netID=0;\n\t\t    if (db[5]==true) netID=netID+1024;\n\t\t    if (db[6]==true) netID=netID+512;\n\t\t    if (db[7]==true) netID=netID+256;\n\t\t    if (db[8]==true) netID=netID+128;\n\t\t    if (db[9]==true) netID=netID+64;\n\t\t    if (db[10]==true) netID=netID+32;\n\t\t\tif (db[11]==true) netID=netID+16;\n\t\t    if (db[12]==true) netID=netID+8;\n\t\t    if (db[13]==true) netID=netID+4;\n\t\t\tif (db[14]==true) netID=netID+2;\n\t\t\tif (db[15]==true) netID++;\n\t\t\t// Bits 16,17,18,19,20,21,22,23 appear to be the site ID\n\t\t\t// Site ID\n\t\t\tif (db[16]==true) siteID=128;\n\t\t\telse siteID=0;\n\t\t\tif (db[17]==true) siteID=siteID+64;\n\t\t\tif (db[18]==true) siteID=siteID+32;\n\t\t\tif (db[19]==true) siteID=siteID+16;\n\t\t\tif (db[20]==true) siteID=siteID+8;\n\t\t\tif (db[21]==true) siteID=siteID+4;\n\t\t\tif (db[22]==true) siteID=siteID+2;\n\t\t\tif (db[23]==true) siteID++;\t\t\t\n\t\t\t// Bits 24,25,26,27 have an unknown purpose\n\t\t\tdline.append(netID);\n\t\t\tdline.append(\" Site: \");\n\t\t\tdline.append(siteID);\n\t\t\t// Make up a status bar system label display\n\t\t\tif (TtheApp!=null) TtheApp.setSystemLabel(\"System : Connect Plus (Network \"+Integer.toString(netID)+\") (Site \"+Integer.toString(siteID)+\")\");\n\t\t}\n\t\t// Connect Plus SLCO 10\n\t\telse if (slco==10)\t{\n\t\t\tint netID,siteID;\n\t\t\tdline.append(\"Connect Plus Control Channel SLCO=\"+Integer.toString(slco)+\" Network: \");\n\t\t\t// Network ID\n\t\t\tif (db[4]==true) netID=2048;\n\t\t\telse netID=0;\n\t\t    if (db[5]==true) netID=netID+1024;\n\t\t    if (db[6]==true) netID=netID+512;\n\t\t    if (db[7]==true) netID=netID+256;\n\t\t    if (db[8]==true) netID=netID+128;\n\t\t    if (db[9]==true) netID=netID+64;\n\t\t    if (db[10]==true) netID=netID+32;\n\t\t\tif (db[11]==true) netID=netID+16;\n\t\t    if (db[12]==true) netID=netID+8;\n\t\t    if (db[13]==true) netID=netID+4;\n\t\t\tif (db[14]==true) netID=netID+2;\n\t\t\tif (db[15]==true) netID++;\n\t\t\t// Bits 16,17,18,19,20,21,22,23 appear to be the site ID\n\t\t\t// Site ID\n\t\t\tif (db[16]==true) siteID=128;\n\t\t\telse siteID=0;\n\t\t\tif (db[17]==true) siteID=siteID+64;\n\t\t\tif (db[18]==true) siteID=siteID+32;\n\t\t\tif (db[19]==true) siteID=siteID+16;\n\t\t\tif (db[20]==true) siteID=siteID+8;\n\t\t\tif (db[21]==true) siteID=siteID+4;\n\t\t\tif (db[22]==true) siteID=siteID+2;\n\t\t\tif (db[23]==true) siteID++;\n\t\t\t// Bits 24,25,26,27 have an unknown purpose\n\t\t\tdline.append(netID);\n\t\t\tdline.append(\" Site: \");\n\t\t\tdline.append(siteID);\n\t\t\t// Make up a status bar system label display\n\t\t\tif (TtheApp!=null) TtheApp.setSystemLabel(\"System : Connect Plus (Network \"+Integer.toString(netID)+\") (Site \"+Integer.toString(siteID)+\")\");\n\t\t}\n\t\t// Capacity Plus SLCO 15\n\t\telse if (slco==15)\t{\n\t\t\tint lcn;\n\t\t\tif (db[16]==true) lcn=8;\n\t\t\telse lcn=0;\n\t\t\tif (db[17]==true) lcn=lcn+4;\n\t\t\tif (db[18]==true) lcn=lcn+2;\n\t\t\tif (db[19]==true) lcn++;\n\t\t\tdline.append(\" Capacity Plus Act_Updt - Rest Channel is LCN \"+Integer.toString(lcn));\n\t\t}\n\t\telse\t{\n\t\t\tdline.append(\"Unknown SLCO=\"+Integer.toString(slco)+\" \");\n\t\t\tfor (a=4;a<28;a++)\t{\n\t\t\t\tif (db[a]==true) dline.append(\"1\");\n\t\t\t\telse dline.append(\"0\");\n\t\t\t}\n\t\t}\n\t\treturn dline.toString();\n\t}\n\t\n\t// Decode a 4 bit section of an Act_Updt\n\tprivate String decodeAct_Updt (int inf,int channel)\t{\n\t\tString l;\n\t\tif (inf==0)\t{\n\t\t\tl=\"No activity on BS time slot \"+Integer.toString(channel);\n\t\t\tif (channel==1) TtheApp.setCh1Label(\"Unused\",TtheApp.labelQuiteColour);\n\t\t\telse if (channel==2) TtheApp.setCh2Label(\"Unused\",TtheApp.labelQuiteColour);\n\t\t}\n\t\telse if (inf==2) l=\"Group CSBK activity on BS time slot \"+Integer.toString(channel);\n\t\telse if (inf==3) l=\"Individual CSBK activity on BS time slot \"+Integer.toString(channel);\n\t\telse if (inf==8) l=\"Group voice activity on BS time slot \"+Integer.toString(channel);\n\t\telse if (inf==9) l=\"Individual voice activity on BS time slot \"+Integer.toString(channel);\n\t\telse if (inf==10) l=\"Individual data activity on BS time slot \"+Integer.toString(channel);\n\t\telse if (inf==11) l=\"Group data activity on BS time slot \"+Integer.toString(channel);\n\t\telse if (inf==12) l=\"Emergency group activity on BS time slot \"+Integer.toString(channel);\n\t\telse if (inf==13) l=\"Emergency individual voice activity on BS time slot \"+Integer.toString(channel);\n\t\telse l=\"Reserved\";\n\t\treturn l;\n\t}\n\t\n\tpublic void setApp (DMRDecode theApp)\t{\n\t\tTtheApp=theApp;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/SlotType.java",
    "content": "package com.dmr;\n\npublic class SlotType {\n\tprivate int dataType;\n\tprivate String line;\n\tprivate boolean passErrorCheck;\n\tprivate DMRDecode theApp;\n\t\n\t// Decode a SLOT TYPE field given a int array of dibit values\n\tpublic String decode (DMRDecode ttheApp,byte[] dibit_buf)\t{\n\t\ttheApp=ttheApp;\n\t\tpassErrorCheck=mainDecode(dibit_buf);\n\t\treturn line;\n\t}\n\t\n\t// The main decode and display method\n\tprivate boolean mainDecode (byte[] dibit_buf)\t{\n\t\tint a,r,colourCode;\n\t\tboolean dataSLOT[]=new boolean[20];\n\t\tStringBuilder sb=new StringBuilder(250);\n\t\t// Convert from dibits into boolean\n\t\t// DATA SLOT is broken into 2 parts either side of the sync burst\n\t\t// these need reuniting into a single 20 bit boolean array\n\t\tr=0;\n\t\tfor (a=61;a<66;a++)\t{\n\t\t\tif (dibit_buf[a]==0)\t{\n\t\t\t\tdataSLOT[r]=false;\n\t\t\t\tdataSLOT[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==1)\t{\n\t\t\t\tdataSLOT[r]=false;\n\t\t\t\tdataSLOT[r+1]=true;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==2)\t{\n\t\t\t\tdataSLOT[r]=true;\n\t\t\t\tdataSLOT[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==3)\t{\n\t\t\t\tdataSLOT[r]=true;\n\t\t\t\tdataSLOT[r+1]=true;\n\t\t\t}\n\t\t\tr=r+2;\n\t\t}\n\t\tfor (a=90;a<95;a++)\t{\n\t\t\tif (dibit_buf[a]==0)\t{\n\t\t\t\tdataSLOT[r]=false;\n\t\t\t\tdataSLOT[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==1)\t{\n\t\t\t\tdataSLOT[r]=false;\n\t\t\t\tdataSLOT[r+1]=true;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==2)\t{\n\t\t\t\tdataSLOT[r]=true;\n\t\t\t\tdataSLOT[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==3)\t{\n\t\t\t\tdataSLOT[r]=true;\n\t\t\t\tdataSLOT[r+1]=true;\n\t\t\t}\n\t\t\tr=r+2;\n\t\t}\n\t\t// Error check SLOT TYPE\n\t\tif (checkGolay208(dataSLOT)==false) return false;\n\t\t// Colour code\n\t\tif (dataSLOT[0]==true) colourCode=8;\n\t\telse colourCode=0;\n\t\tif (dataSLOT[1]==true) colourCode=colourCode+4;\n\t\tif (dataSLOT[2]==true) colourCode=colourCode+2;\n\t\tif (dataSLOT[3]==true) colourCode++;\n\t\t// If this has changed then set the main colourCode variable\n\t\tif (theApp!=null) theApp.setColourCode(colourCode);\n\t\t// Data Type\n\t\tif (dataSLOT[4]==true) dataType=8;\n\t\telse dataType=0;\n\t\tif (dataSLOT[5]==true) dataType=dataType+4;\n\t\tif (dataSLOT[6]==true) dataType=dataType+2;\n\t\tif (dataSLOT[7]==true) dataType++;\n\t\t// Display this info\n\t\tsb.append(\"Slot Type : Colour Code \"+Integer.toString(colourCode));\n\t\tif (dataType==0) sb.append(\" PI Header\");\n\t\telse if (dataType==1) sb.append(\" Voice LC Header\");\n\t\telse if (dataType==2) sb.append(\" Terminator with LC\");\n\t\telse if (dataType==3) sb.append(\" CSBK\");\n\t\telse if (dataType==4) sb.append(\" MBC Header\");\n\t\telse if (dataType==5) sb.append(\" MBC Continuation\");\n\t\telse if (dataType==6) sb.append(\" Data Header\");\n\t\telse if (dataType==7) sb.append(\" Rate  Data Continuation\");\n\t\telse if (dataType==8) sb.append(\" Rate  Data Continuation\");\n\t\telse if (dataType==9) sb.append(\" Idle\");\n\t\telse sb.append(\" Reserved for future use\");\n\t\t// Convert from StringBuilder to a String\n\t\tline=sb.toString();\n\t\treturn true;\n\t}\n\t\n\t// Code to calculate all valid values for Golay (20,8)\n\tboolean calcGolay208 ()\t{\n\t\tboolean d[]=new boolean[8];\n\t\tboolean p[]=new boolean[12];\n\t\tint value[]=new int[256];\n\t\tint a;\n\t\t// Run through all possible 8 bit values\n\t\tfor (a=0;a<256;a++){\n\t\t\t// Convert to binary\n\t\t\tif ((a&128)>0) d[0]=true;\n\t\t\telse d[0]=false;\n\t\t\tif ((a&64)>0) d[1]=true;\n\t\t\telse d[1]=false;\n\t\t\tif ((a&32)>0) d[2]=true;\n\t\t\telse d[2]=false;\n\t\t\tif ((a&16)>0) d[3]=true;\n\t\t\telse d[3]=false;\n\t\t\tif ((a&8)>0) d[4]=true;\n\t\t\telse d[4]=false;\n\t\t\tif ((a&4)>0) d[5]=true;\n\t\t\telse d[5]=false;\n\t\t\tif ((a&2)>0) d[6]=true;\n\t\t\telse d[6]=false;\n\t\t\tif ((a&1)>0) d[7]=true;\n\t\t\telse d[7]=false;\n\t\t\t// Shift the value 12 times to the left\n\t\t\tvalue[a]=a<<12;\n\t\t\t// Calculate the parity bits\n\t\t\tp[0]=d[1]^d[4]^d[5]^d[6]^d[7];\n\t\t\tp[1]=d[1]^d[2]^d[4];\n\t\t\tp[2]=d[0]^d[2]^d[3]^d[5];\n\t\t\tp[3]=d[0]^d[1]^d[3]^d[4]^d[6];\n\t\t\tp[4]=d[0]^d[1]^d[2]^d[4]^d[5]^d[7];\n\t\t\tp[5]=d[0]^d[2]^d[3]^d[4]^d[7];\n\t\t\tp[6]=d[3]^d[6]^d[7];\n\t\t\tp[7]=d[0]^d[1]^d[5]^d[6];\n\t\t\tp[8]=d[0]^d[1]^d[2]^d[6]^d[7];\n\t\t\tp[9]=d[2]^d[3]^d[4]^d[5]^d[6];\n\t\t\tp[10]=d[0]^d[3]^d[4]^d[5]^d[6]^d[7];\n\t\t\tp[11]=d[1]^d[2]^d[3]^d[5]^d[7];\n\t\t\t// Add these to the lower bits of the valid words\n\t\t\tif (p[0]==true) value[a]=value[a]+2048;\n\t\t\tif (p[1]==true) value[a]=value[a]+1024;\n\t\t\tif (p[2]==true) value[a]=value[a]+512;\n\t\t\tif (p[3]==true) value[a]=value[a]+256;\n\t\t\tif (p[4]==true) value[a]=value[a]+128;\n\t\t\tif (p[5]==true) value[a]=value[a]+64;\n\t\t\tif (p[6]==true) value[a]=value[a]+32;\n\t\t\tif (p[7]==true) value[a]=value[a]+16;\n\t\t\tif (p[8]==true) value[a]=value[a]+8;\n\t\t\tif (p[9]==true) value[a]=value[a]+4;\n\t\t\tif (p[10]==true) value[a]=value[a]+2;\n\t\t\tif (p[11]==true) value[a]=value[a]+1;\n\t\t}\n\t\t// Just something to break on !\n\t\treturn true;\n\t}\n\t\n\t// Check if a 20 bit boolean array has the collect Golay (20,8) coding\n\tprivate boolean checkGolay208 (boolean[] word)\t{\n\t\tint a,golayValue;\n\t\t// A complete list of valid slot type words\n\t\t// This was generated by the calcGolay208 () method\n\t\tfinal int[]GolayNums={0, 6379, 10558, 12757, 19095, 21116, 25513, 31554, 36294, 38189, \n\t\t\t\t42232, 48147, 51025, 57274, 61039, 63108, 66407, 72588, 76377, 78514, 84464, \n\t\t\t\t86299, 90318, 96293, 102049, 104010, 108447, 114548, 115766, 122077, 126216, \n\t\t\t\t128483, 132813, 138790, 143347, 145176, 150618, 152753, 157028, 163215, 166667, \n\t\t\t\t168928, 172597, 178910, 180636, 186743, 190626, 192585, 198058, 204097, 208020, \n\t\t\t\t210047, 216893, 219094, 222723, 229096, 231532, 233607, 237906, 244153, 246523, \n\t\t\t\t252432, 256965, 258862, 265625, 267634, 271527, 277580, 280334, 286693, 290352, \n\t\t\t\t292571, 295007, 301236, 305505, 307594, 314056, 315939, 320502, 326429, 331518,\n\t\t\t\t333333, 337856, 343851, 345193, 351362, 355671, 357820, 361272, 367571, 371206, \n\t\t\t\t373485, 379311, 381252, 385169, 391290, 396116, 398271, 402026, 408193, 410051, \n\t\t\t\t416040, 420093, 421910, 427666, 433785, 438188, 440135, 445445, 447726, 451899, \n\t\t\t\t458192, 460851, 463064, 467213, 473574, 475812, 481871, 486298, 488305, 493045, \n\t\t\t\t498974, 502987, 504864, 511842, 513929, 517724, 523959, 525274, 531249, 535268, \n\t\t\t\t537103, 543053, 545190, 548979, 555160, 560668, 562935, 567074, 573385, 574603, \n\t\t\t\t580704, 585141, 587102, 590013, 596054, 600451, 602472, 608810, 611009, 615188, \n\t\t\t\t621567, 626043, 628112, 631877, 638126, 641004, 646919, 650962, 652857, 656663, \n\t\t\t\t663036, 666665, 668866, 675712, 677739, 681662, 687701, 690385, 692282, 696815, \n\t\t\t\t702724, 705094, 711341, 715640, 717715, 722544, 728731, 733006, 735141, 740583, \n\t\t\t\t742412, 746969, 752946, 756662, 758621, 762504, 768611, 770337, 776650, 780319, \n\t\t\t\t782580, 790083, 792232, 796541, 802710, 804052, 810047, 814570, 816385, 820101, \n\t\t\t\t826222, 830139, 832080, 837906, 840185, 843820, 850119, 855332, 857551, 861210, \n\t\t\t\t867569, 870323, 876376, 880269, 882278, 884962, 890889, 895452, 897335, 903797, \n\t\t\t\t905886, 910155, 916384, 919694, 921701, 926128, 932187, 934425, 940786, 944935, \n\t\t\t\t947148, 951624, 957859, 961654, 963741, 970719, 972596, 976609, 982538, 986089, \n\t\t\t\t987906, 991959, 997948, 999806, 1005973, 1009728, 1011883, 1017391, 1023684, \n\t\t\t\t1027857, 1030138, 1035448, 1037395, 1041798, 1047917};\n\t\t// Convert the boolean array into an integer\n\t\tif (word[19]==true) golayValue=1;\n\t\telse golayValue=0;\n\t\tif (word[18]==true) golayValue=golayValue+2;\n\t\tif (word[17]==true) golayValue=golayValue+4;\n\t\tif (word[16]==true) golayValue=golayValue+8;\n\t\tif (word[15]==true) golayValue=golayValue+16;\n\t\tif (word[14]==true) golayValue=golayValue+32;\n\t\tif (word[13]==true) golayValue=golayValue+64;\n\t\tif (word[12]==true) golayValue=golayValue+128;\n\t\tif (word[11]==true) golayValue=golayValue+256;\n\t\tif (word[10]==true) golayValue=golayValue+512;\n\t\tif (word[9]==true) golayValue=golayValue+1024;\n\t\tif (word[8]==true) golayValue=golayValue+2048;\n\t\tif (word[7]==true) golayValue=golayValue+4096;\n\t\tif (word[6]==true) golayValue=golayValue+8192;\n\t\tif (word[5]==true) golayValue=golayValue+16384;\n\t\tif (word[4]==true) golayValue=golayValue+32768;\n\t\tif (word[3]==true) golayValue=golayValue+65536;\n\t\tif (word[2]==true) golayValue=golayValue+131072;\n\t\tif (word[1]==true) golayValue=golayValue+262144;\n\t\tif (word[0]==true) golayValue=golayValue+524288;\n\t\t// Run through the possible values and we have a match return true\n\t\tfor (a=0;a<256;a++)\t{\n\t\t\tif (golayValue==GolayNums[a]) return true;\n\t\t}\n\t\t// No matches so we must have a problem and so should return false\n\t\treturn false;\n\t}\n\n\t// Let the main program know if there is an error in the frame\n\tpublic boolean isPassErrorCheck() {\n\t\treturn passErrorCheck;\n\t}\n\t\n\t// Return the data type\n\tpublic int returnDataType ()\t{\n\t\treturn dataType;\n\t}\n\t\t\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/SocketOut.java",
    "content": "package com.dmr;\n\nimport java.io.OutputStreamWriter;\nimport java.io.PrintWriter;\nimport java.net.ServerSocket;\nimport java.net.Socket;\n\npublic class SocketOut implements Runnable {\n\tprivate boolean ready;\n\tprivate static int PORT=17887;\n\tprivate static int MAXCONNS=10;\n\tprivate ServerSocket serversocket;\n\tprivate Socket socket[]=new Socket[MAXCONNS];\n\tprivate boolean socketStatus[]=new boolean[MAXCONNS];\n\tprivate PrintWriter socketPrintWriter[]=new PrintWriter[MAXCONNS];\n\n\tpublic SocketOut (DMRDecode theApp) {\n    \tready=false;\n      }\n\t\n\t// Main\n    public void run()\t{\n    \tint next;\n    \t// Run continously\n    \tfor (;;)\t{\n    \t\t// Wait for a socket connection if the listening socket has been setup\n    \t\t// and there is a free socket available\n            if(ready == false){\n                try{\n                    Thread.sleep(1000);\n                }catch(InterruptedException ie){\n                }\n                continue;\n            }\n    \t\tif ((ready==true)&&(checkForFreeSockets()==true))\t{\n    \t\t\t// Get the index of the next available socket\n    \t\t\tnext=nextFreeSocket();\n    \t\t\t// Wait for a connection and when it arrives use the free socket\n        \t\twaitForConnection(next);\t\n     \t\t}\n     \t}\n    }\n    \n    // Setup a listening TCP/IP socket\n    public boolean setupSocket()\t{\n    \ttry\t{\n    \t\tserversocket=new ServerSocket(PORT);\n    \t} catch (Exception e)\t{\n    \t\treturn false;\n    \t}\n    \tready=true;\n    \treturn true;\n    }\n    \n    // Wait for a connection to the next free socket\n    private void waitForConnection(int n)\t{\n    \ttry\t{\n    \t\t// Wait for the connection\n    \t\tsocket[n]=serversocket.accept();\n    \t\t// Assign a PrintWriter to this socket\n    \t\tsocketPrintWriter[n]=new PrintWriter(new OutputStreamWriter(socket[n].getOutputStream(),\"8859_1\"));\n    \t\t// Send \"OK\" to the connected client\n    \t\tsocketPrintWriter[n].println(\"OK\");\n    \t\tsocketPrintWriter[n].flush();\n    \t} catch (Exception e)\t{\n    \t\treturn;\n    \t}\n    \tsocketStatus[n]=true;\n    }\n    \n    // Return the next free socket\n    private int nextFreeSocket()\t{\n    \tint a;\n    \tfor (a=0;a<MAXCONNS;a++)\t{\n    \t\tif (socketStatus[a]==false) return a;\n    \t}\n    \t// Return -1 if no sockets are free\n    \treturn -1;\n    }\n    \n    // Check if there are any sockets free\n    private boolean checkForFreeSockets()\t{\n    \tint a;\n    \tfor (a=0;a<MAXCONNS;a++)\t{\n    \t\tif (socketStatus[a]==false) return true;\n    \t}\n    \t// Return false if no sockets are free\n    \treturn false;\n    }\n    \n    // Send voice data to connected clients\n    public void sendVoiceViaSocket (int vdata[],int channel)\t{\n    \tint a,b;\n    \tfor (a=0;a<vdata.length;a++)\t{\n    \t\t// Run through all the possible sockets\n    \t\tfor (b=0;b<MAXCONNS;b++)\t{\n    \t\t\ttry\t{\n    \t\t\t\tif (socketStatus[b]==true)\t{\n    \t\t\t\t\tif (a==0)\t{\n    \t\t\t\t\t\t// Send a # to show the start of the voice frame\n    \t\t\t\t\t\tsocketPrintWriter[b].println(\"#\");\n    \t\t\t\t\t\t// Send the channel number at the start of the frame\n    \t\t\t\t\t\tsocketPrintWriter[b].println(channel);\n    \t\t\t\t\t}\n    \t\t\t\t\tsocketPrintWriter[b].println(vdata[a]);\n    \t\t\t\t\t// If this is the last int of the voice frame flush the stream\n    \t\t\t\t\t// this way there is no delay in sending the data\n    \t\t\t\t\tif (a==(vdata.length-1)) socketPrintWriter[b].flush();\n    \t\t\t\t}\n    \t\t\t} catch (Exception e)\t{\n    \t\t\t\t// If we have a problem this socket can't be valid anymore\n    \t\t\t\tsocketStatus[b]=false;\n    \t\t\t}\n    \t\t}\n    \t}\n    }\n    \n\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/TextfileFilter.java",
    "content": "package com.dmr;\n\nimport java.io.File;\n\n//This class extends filechoose so only .txt files can be selected\npublic class TextfileFilter extends javax.swing.filechooser.FileFilter {\n\tpublic boolean accept(File f) {\n\t\t// if it is a directory -- we want to show it so return true.\n\t\tif (f.isDirectory())\n\t\t\treturn true;\n\t\t// get the extension of the file\n\t\tString extension = getExtension(f);\n\t\t// check to see if the extension is equal to \"txt\"\n\t\tif (extension.equals(\"txt\"))\n\t\t\treturn true;\n\t\t// default -- fall through. False is return on all\n\t\t// occasions except:\n\t\t// a) the file is a directory\n\t\t// b) the file's extension is what we are looking for.\n\t\treturn false;\n\t}\n\n\t/**\n\t * Again, this is declared in the abstract class The description of this\n\t * filter\n\t */\n\tpublic String getDescription() {\n\t\treturn \"TXT files\";\n\t}\n\n\t/**\n\t * Method to get the extension of the file, in lowercase\n\t */\n\tprivate String getExtension(File f) {\n\t\tString s=f.getName();\n\t\tint i=s.lastIndexOf('.');\n\t\tif (i>0&&i<s.length()-1) return s.substring(i+1).toLowerCase();\n\t\telse return \"\";\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/com/dmr/Trellis.java",
    "content": "package com.dmr;\n\npublic class Trellis {\n\t\n\tprivate final int INTERLEAVE[]={\n\t\t\t0,1,8,9,16,17,24,25,32,33,40,41,48,49,56,57,64,65,72,73,80,81,88,89,96,97,\n\t\t\t2,3,10,11,18,19,26,27,34,35,42,43,50,51,58,59,66,67,74,75,82,83,90,91,\n\t\t\t4,5,12,13,20,21,28,29,36,37,44,45,52,53,60,61,68,69,76,77,84,85,92,93,\n\t\t\t6,7,14,15,22,23,30,31,38,39,46,47,54,55,62,63,70,71,78,79,86,87,94,95};\n\t\n\tprivate final byte STATETABLE[]={\n\t\t\t0,8,4,12,2,10,6,14,\n\t\t\t4,12,2,10,6,14,0,8,\n\t\t\t1,9,5,13,3,11,7,15,\n\t\t\t5,13,3,11,7,15,1,9,\n\t\t\t3,11,7,15,1,9,5,13,\n\t\t\t7,15,1,9,5,13,3,11,\n\t\t\t2,10,6,14,0,8,4,12,\n\t\t\t6,14,0,8,4,12,2,10};\n\t\t\t\n\t// Converts the 3/4 rate trellis encoded bits to plain binary\n\tpublic boolean[] decode (boolean r[])\t{\n\t\tbyte dibits[]=extractDibits(r);\n\t\tbyte cons[]=constellationOut(dibits);\n\t\tint tri[]=tribitExtract(cons);\n\t\t// If the output of tribitExtract() is null then we have an error so return null\n\t\tif (tri==null) return null;\n\t\tboolean out[]=binaryConvert(tri);\n\t\treturn out;\n\t}\n\t\n\t// Extract and deinterleave the dibits\n\tprivate byte[] extractDibits (boolean[] rawBits)\t{\n\t\tint a,index=0,deinterleave;\n\t\tbyte trellisDibit=0;\n\t\tbyte encDibit[]=new byte[98];\n\t\tfor (a=0;a<196;a=a+2)\t{\n\t\t\t// Set the dibits\n\t\t\t// 01 = +3\n\t\t\t// 00 = +1\n\t\t\t// 10 = -1\n\t\t\t// 11 = -3\n\t\t\tif ((rawBits[a]==false)&&(rawBits[a+1]==true)) trellisDibit=+3;\n\t\t\telse if ((rawBits[a]==false)&&(rawBits[a+1]==false)) trellisDibit=+1;\n\t\t\telse if ((rawBits[a]==true)&&(rawBits[a+1]==false)) trellisDibit=-1;\n\t\t\telse if ((rawBits[a]==true)&&(rawBits[a+1]==true)) trellisDibit=-3;\n\t\t\t// Deinterleave\n\t\t\tdeinterleave=INTERLEAVE[index];\n\t\t\tencDibit[deinterleave]=trellisDibit;\n\t\t\t// Increase the index\n\t\t\tindex++;\n\t\t}\n\t\treturn encDibit;\n\t}\n\t\n\t// Extract the constellation points\n\tprivate byte[] constellationOut (byte[] encDibit)\t{\n\t\tbyte constellationPoints[]=new byte[49];\n\t\tint a,index=0;\n\t\tfor (a=0;a<98;a=a+2)\t{\n\t\t\tif ((encDibit[a]==+1)&&(encDibit[a+1]==-1)) constellationPoints[index]=0;\n\t\t\telse if ((encDibit[a]==-1)&&(encDibit[a+1]==-1)) constellationPoints[index]=1;\n\t\t\telse if ((encDibit[a]==+3)&&(encDibit[a+1]==-3)) constellationPoints[index]=2;\n\t\t\telse if ((encDibit[a]==-3)&&(encDibit[a+1]==-3)) constellationPoints[index]=3;\n\t\t\telse if ((encDibit[a]==-3)&&(encDibit[a+1]==-1)) constellationPoints[index]=4;\n\t\t\telse if ((encDibit[a]==+3)&&(encDibit[a+1]==-1)) constellationPoints[index]=5;\n\t\t\telse if ((encDibit[a]==-1)&&(encDibit[a+1]==-3)) constellationPoints[index]=6;\n\t\t\telse if ((encDibit[a]==+1)&&(encDibit[a+1]==-3)) constellationPoints[index]=7;\n\t\t\telse if ((encDibit[a]==-3)&&(encDibit[a+1]==+3)) constellationPoints[index]=8;\n\t\t\telse if ((encDibit[a]==+3)&&(encDibit[a+1]==+3)) constellationPoints[index]=9;\n\t\t\telse if ((encDibit[a]==-1)&&(encDibit[a+1]==+1)) constellationPoints[index]=10;\n\t\t\telse if ((encDibit[a]==+1)&&(encDibit[a+1]==+1)) constellationPoints[index]=11;\n\t\t\telse if ((encDibit[a]==+1)&&(encDibit[a+1]==+3)) constellationPoints[index]=12;\n\t\t\telse if ((encDibit[a]==-1)&&(encDibit[a+1]==+3)) constellationPoints[index]=13;\n\t\t\telse if ((encDibit[a]==+3)&&(encDibit[a+1]==+1)) constellationPoints[index]=14;\n\t\t\telse if ((encDibit[a]==-3)&&(encDibit[a+1]==+1)) constellationPoints[index]=15;\n\t\t\tindex++;\n\t\t}\n\t\treturn constellationPoints;\n\t}\n\n\t// Extract tribits (as ints) from the constellation points\n\tprivate int[] tribitExtract (byte cons[])\t{\n\t\tint a,b,rowStart,lastState=0;\n\t\tint tribit[]=new int[49];\n\t\tfor (a=0;a<cons.length;a++)\t{\n\t\t\t// The lastState variable decides which row of STATETABLE we should use\n\t\t\trowStart=lastState*8;\n\t\t\tboolean match=false;\n\t\t\tfor (b=rowStart;b<(rowStart+8);b++)\t{\n\t\t\t\t// Check if this constellation point matches an element of this row of STATETABLE\n\t\t\t\tif (cons[a]==STATETABLE[b])\t{\n\t\t\t\t\t// Yes it does\n\t\t\t\t\tmatch=true;\n\t\t\t\t\tlastState=b-rowStart;\n\t\t\t\t\ttribit[a]=lastState;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If no match found then we have a problem\n\t\t\tif (match==false) return null;\n\t\t}\n\t\treturn tribit;\n\t}\n\t\n\t// Extract the 144 binary bits from the dibits\n\tprivate boolean[] binaryConvert (int tribit[])\t{\n\t\tint a,b=0;\n\t\tboolean out[]=new boolean[144];\n\t\tfor (a=0;a<144;a=a+3)\t{\n\t\t\t// Convert three bits at a time\n\t\t\tif ((tribit[b]&4)>0) out[a]=true;\n\t\t\telse  out[a]=false;\n\t\t\tif ((tribit[b]&2)>0) out[a+1]=true;\n\t\t\telse  out[a+1]=false;\n\t\t\tif ((tribit[b]&1)>0) out[a+2]=true;\n\t\t\telse  out[a+2]=false;\n\t\t\t// Increment the bit counter\n\t\t\tb++;\n\t\t}\n\t\treturn out;\n\t}\n\n\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/UsersLogged.java",
    "content": "package com.dmr;\n\npublic class UsersLogged {\n\tprivate static final int MAX=8192;\n\tprivate int userCounter=0;\n\tprivate int ident[]=new int[MAX];\n\tprivate boolean group[]=new boolean[MAX];\n\tprivate boolean dataUser[]=new boolean[MAX];\n\tprivate boolean groupCallUser[]=new boolean[MAX];\n\tprivate boolean unitCallUser[]=new boolean[MAX];\n\tprivate boolean usedChannel1[]=new boolean[MAX];\n\tprivate boolean usedChannel2[]=new boolean[MAX];\n\t\n\t// Adds a user and returns TRUE if this has been done\n\tpublic boolean addUser (int tident)\t{\n\t\tint a;\n\t\t// Check the buffer isn't full\n\t\tif (userCounter==(MAX-1)) return false;\n\t\t// Check if the user already exists\n\t\tfor (a=0;a<userCounter;a++)\t{\n\t\t\tif (ident[a]==tident) return false;\n\t\t}\n\t\t// No add them\n\t\tident[userCounter]=tident;\n\t\tgroup[userCounter]=false;\n\t\tdataUser[userCounter]=false;\n\t\tgroupCallUser[userCounter]=false;\n\t\tunitCallUser[userCounter]=false;\n\t\tusedChannel1[userCounter]=false;\n\t\tusedChannel2[userCounter]=false;\n\t\t// Increment index\n\t\tuserCounter++;\n\t\treturn true;\n\t}\n\t\n\t// Returns a users index number or -1 if not found\n\tpublic int findUserIndex (int tident)\t{\n\t\tint a;\n\t\t// Check if the user already exists\n\t\tfor (a=0;a<userCounter;a++)\t{\n\t\t\tif (ident[a]==tident) return a;\n\t\t}\n\t\t// Return -1 as nothing found\n\t\treturn -1;\n\t}\n\t\n\t// Sets an ident as being a group\n\tpublic void setAsGroup (int tident)\t\t{\n\t\tgroup[tident]=true;\n\t}\n\t\n\t// Sets an ident as being a data user\n\tpublic void setAsDataUser (int tident)\t\t{\n\t\tdataUser[tident]=true;\n\t}\n\t\n\t// Sets an ident as being a group call user\n\tpublic void setAsGroupUser (int tident)\t\t{\n\t\tgroupCallUser[tident]=true;\n\t}\n\t\n\t// Sets an ident as being a unit to unit caller\n\tpublic void setAsUnitUser (int tident)\t\t{\n\t\tunitCallUser[tident]=true;\n\t}\n\t\n\t// Shows how many users have been logged\n\tpublic int returnUserCounter ()\t{\n\t\treturn userCounter;\n\t}\n\t\n\t// Records which channels as radio has used\n\tpublic void setChannel (int tident,int channel)\t{\n\t\tif (channel==1) usedChannel1[tident]=true;\n\t\telse usedChannel2[tident]=true;\n\t}\n\t\n\t// Sort the users by mobile ident //\n\t// This code is standard bubble sort taken from the book\n\t// \"Learning to Program in C\" by N.Kantaris //\n\tpublic void sortByIdent() {\n\t\tint i,j,temp,max;\n\t\tboolean flag,btemp;\n\t\tmax=userCounter;\n\t\tfor (i=0;i<userCounter-1;i++) {\n\t\t\tmax--;\n\t\t\tflag=false;\n\t\t\tfor (j=0;j<max;j++)\n\t\t\t\tif (ident[j]>ident[j+1]) {\n\t\t\t\t\t// Ident //\n\t\t\t\t\ttemp=ident[j];\n\t\t\t\t\tident[j]=ident[j+1];\n\t\t\t\t\tident[j+1]=temp;\n\t\t\t\t\t// Group/User\n\t\t\t\t\tbtemp=group[j];\n\t\t\t\t\tgroup[j]=group[j+1];\n\t\t\t\t\tgroup[j+1]=btemp;\n\t\t\t\t\t// Data User\n\t\t\t\t\tbtemp=dataUser[j];\n\t\t\t\t\tdataUser[j]=dataUser[j+1];\n\t\t\t\t\tdataUser[j+1]=btemp;\n\t\t\t\t\t// Group Call User\n\t\t\t\t\tbtemp=groupCallUser[j];\n\t\t\t\t\tgroupCallUser[j]=groupCallUser[j+1];\n\t\t\t\t\tgroupCallUser[j+1]=btemp;\n\t\t\t\t\t// Unit Call User\n\t\t\t\t\tbtemp=unitCallUser[j];\n\t\t\t\t\tunitCallUser[j]=unitCallUser[j+1];\n\t\t\t\t\tunitCallUser[j+1]=btemp;\n\t\t\t\t\t// Used Channel 1\n\t\t\t\t\tbtemp=usedChannel1[j];\n\t\t\t\t\tusedChannel1[j]=usedChannel1[j+1];\n\t\t\t\t\tusedChannel1[j+1]=btemp;\n\t\t\t\t\t// Used Channel 2\n\t\t\t\t\tbtemp=usedChannel2[j];\n\t\t\t\t\tusedChannel2[j]=usedChannel2[j+1];\n\t\t\t\t\tusedChannel2[j+1]=btemp;\n\t\t\t\t\tflag=true;\n\t\t\t\t}\n\t\t\tif (flag==false) break;\n\t\t}\n\t}\n\t\n\t// Return a formatted info line\n\tpublic String returnInfo (int index)\t{\n\t\tint items=0;\n\t\tString l=Integer.toString(ident[index]);\n\t\tif (group[index]==true) l=l+\" GROUP\";\n\t\tif (dataUser[index]==true)\t{\n\t\t\titems++;\n\t\t\tl=l+\" Data \";\n\t\t}\n\t\tif (groupCallUser[index]==true)\t{\n\t\t\tif (items>0) l=l+\"+\";\n\t\t\titems++;\n\t\t\tl=l+\" Group Calls \";\n\t\t}\n\t\tif (unitCallUser[index]==true)\t{\n\t\t\tif (items>0) l=l+\"+\";\n\t\t\titems++;\n\t\t\tl=l+\" Unit to Unit Calls \";\n\t\t}\n\t\tif (items==1) l=l+\"only\";\n\t\t// Channels\n\t\tif ((usedChannel1[index]==true)&&(usedChannel2[index]==false))\tl=l+\" (Only used channel 1)\";\n\t\telse if ((usedChannel2[index]==true)&&(usedChannel1[index]==false))\tl=l+\" (Only used channel 2)\";\n\t\telse if ((usedChannel1[index]==true)&&(usedChannel2[index]==true))\tl=l+\" (Used both channels)\";\n\t\t// All done\n\t\treturn l;\n\t}\n\t\n\t// Clear all stored records\n\tpublic void clearAll()\t{\n\t\tuserCounter=0;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/Utilities.java",
    "content": "package com.dmr;\n\npublic class Utilities {\n\t\n\t// Given a MFID as an int return the manufacturers name as a String\n\tpublic String returnMFIDName (int mfid)\t{\n\t\tif (mfid==0x04) return \"Fylde Micro\";\n\t\telse if (mfid==0x05) return \"PROD-EL SPA\";\n\t\telse if (mfid==0x06) return \"Trident Datacom\";\n\t\telse if (mfid==0x07) return \"RADIODATA\";\n\t\telse if ((mfid==0x08)||(mfid==0x68)) return \"HYT\";\n\t\telse if (mfid==0x10) return \"Motorola\";\n\t\telse if ((mfid==0x13)||(mfid==0x1c)) return \"EMC SPA\";\n\t\telse if ((mfid==0x33)||(mfid==0x3c)) return \"Radio Activity Srl\";\n\t\telse if (mfid==0x58) return \"Tait\";\n\t\telse if (mfid==0x77) return \"Vertex Standard\";\n\t\telse return \"Unknown\";\n\t}\n\t\n\t// Return a 24 bit address \n\tpublic int retAddress (boolean bits[],int offset)\t{\n\t\tint addr=0,a,b,c;\n\t\tfor (a=0;a<24;a++)\t{\n\t\t\tb=(24-a)-1;\n\t\t\tc=(int)Math.pow(2.0,b);\n\t\t\tif (bits[a+offset]==true) addr=addr+c;\n\t\t}\n\t\treturn addr;\n\t}\n\t\n\t// Return an 16 bit byte from a boolean array\n\tpublic int retSixteen (boolean bits[],int offset)\t{\n\t\tint b=0;\n\t\tif (bits[offset]==true) b=32768;\n\t\tif (bits[offset+1]==true) b=b+16384;\n\t\tif (bits[offset+2]==true) b=b+8192;\n\t\tif (bits[offset+3]==true) b=b+4096;\n\t\tif (bits[offset+4]==true) b=b+2048;\n\t\tif (bits[offset+5]==true) b=b+1024;\n\t\tif (bits[offset+6]==true) b=b+512;\n\t\tif (bits[offset+7]==true) b=b+256;\n\t\tif (bits[offset+8]==true) b=b+128;\n\t\tif (bits[offset+9]==true) b=b+64;\n\t\tif (bits[offset+10]==true) b=b+32;\n\t\tif (bits[offset+11]==true) b=b+16;\n\t\tif (bits[offset+12]==true) b=b+8;\n\t\tif (bits[offset+13]==true) b=b+4;\n\t\tif (bits[offset+14]==true) b=b+2;\n\t\tif (bits[offset+15]==true) b++;\n\t\treturn b;\n\t}\t\n\t\n\t// Return an 12 bit byte from a boolean array\n\tpublic int retTwelve (boolean bits[],int offset)\t{\n\t\tint b=0;\n\t\tif (bits[offset]==true) b=2048;\n\t\tif (bits[offset+2]==true) b=b+1024;\n\t\tif (bits[offset+2]==true) b=b+512;\n\t\tif (bits[offset+3]==true) b=b+256;\n\t\tif (bits[offset+4]==true) b=b+128;\n\t\tif (bits[offset+5]==true) b=b+64;\n\t\tif (bits[offset+6]==true) b=b+32;\n\t\tif (bits[offset+7]==true) b=b+16;\n\t\tif (bits[offset+8]==true) b=b+8;\n\t\tif (bits[offset+9]==true) b=b+4;\n\t\tif (bits[offset+10]==true) b=b+2;\n\t\tif (bits[offset+11]==true) b++;\n\t\treturn b;\n\t}\t\t\n\n\t// Return an 9 bit byte from a boolean array\n\tpublic int retNine (boolean bits[],int offset)\t{\n\t\tint b=0;\n\t\tif (bits[offset]==true) b=256;\n\t\tif (bits[offset+1]==true) b=b+128;\n\t\tif (bits[offset+2]==true) b=b+64;\n\t\tif (bits[offset+3]==true) b=b+32;\n\t\tif (bits[offset+4]==true) b=b+16;\n\t\tif (bits[offset+5]==true) b=b+8;\n\t\tif (bits[offset+6]==true) b=b+4;\n\t\tif (bits[offset+7]==true) b=b+2;\n\t\tif (bits[offset+7]==true) b++;\n\t\treturn b;\n\t}\t\t\n\t\n\t// Return an 8 bit byte from a boolean array\n\tpublic int retEight (boolean bits[],int offset)\t{\n\t\tint b=0;\n\t\tif (bits[offset]==true) b=128;\n\t\tif (bits[offset+1]==true) b=b+64;\n\t\tif (bits[offset+2]==true) b=b+32;\n\t\tif (bits[offset+3]==true) b=b+16;\n\t\tif (bits[offset+4]==true) b=b+8;\n\t\tif (bits[offset+5]==true) b=b+4;\n\t\tif (bits[offset+6]==true) b=b+2;\n\t\tif (bits[offset+7]==true) b++;\n\t\treturn b;\n\t}\n\t\n\t// Return an 7 bit byte from a boolean array\n\tpublic int retSeven (boolean bits[],int offset)\t{\n\t\tint b=0;\n\t\tif (bits[offset]==true) b=64;\n\t\tif (bits[offset+1]==true) b=b+32;\n\t\tif (bits[offset+2]==true) b=b+16;\n\t\tif (bits[offset+3]==true) b=b+8;\n\t\tif (bits[offset+4]==true) b=b+4;\n\t\tif (bits[offset+5]==true) b=b+2;\n\t\tif (bits[offset+6]==true) b++;\n\t\treturn b;\n\t}\t\n\t\n\t// Return a 6 bit byte from a boolean array\n\tpublic int retSix (boolean bits[],int offset)\t{\n\t\tint b=0;\n\t\tif (bits[offset]==true) b=32;\n\t\tif (bits[offset+1]==true) b=b+16;\n\t\tif (bits[offset+2]==true) b=b+8;\n\t\tif (bits[offset+3]==true) b=b+4;\n\t\tif (bits[offset+4]==true) b=b+2;\n\t\tif (bits[offset+5]==true) b++;\n\t\treturn b;\n\t}\n\t\n\t// Return a 5 bit byte from a boolean array\n\tpublic int retFive (boolean bits[],int offset)\t{\n\t\tint b=0;\n\t\tif (bits[offset]==true) b=16;\n\t\tif (bits[offset+1]==true) b=b+8;\n\t\tif (bits[offset+2]==true) b=b+4;\n\t\tif (bits[offset+3]==true) b=b+2;\n\t\tif (bits[offset+4]==true) b++;\n\t\treturn b;\n\t}\t\n\t\n\t// Return a 4 bit byte from a boolean array\n\tpublic int retFour (boolean bits[],int offset)\t{\n\t\tint b=0;\n\t\tif (bits[offset]==true) b=8;\n\t\tif (bits[offset+1]==true) b=b+4;\n\t\tif (bits[offset+2]==true) b=b+2;\n\t\tif (bits[offset+3]==true) b++;\n\t\treturn b;\n\t}\t\n\t\n\t// Return a 3 bit byte from a boolean array\n\tpublic int retThree (boolean bits[],int offset)\t{\n\t\tint b=0;\n\t\tif (bits[offset]==true) b=4;\n\t\tif (bits[offset+1]==true) b=b+2;\n\t\tif (bits[offset+2]==true) b++;\n\t\treturn b;\n\t}\t\n\t\n\t// Decode and display Service Options\n\tpublic String decodeServiceOptions (boolean bits[],int offset)\t{\n\t\tint priority;\n\t\tStringBuilder so=new StringBuilder(300);\n\t\tso.append(\"Service Options : \");\n\t\t// Emergency\n\t\tif (bits[offset]==false) so.append(\"Non-emergency\");\n\t\telse so.append(\"Emergency\");\n\t\t// Privacy\n\t\tif (bits[offset+1]==true) so.append(\"/Privacy Enabled\");\n\t\t// +2 and +3 are reserved bits\n\t\t// +4 is Broadcast\n\t\tif (bits[offset+4]==true) so.append(\"/Broadcast\");\n\t\t// +5 is OVCM\n\t\tif (bits[offset+5]==true) so.append(\"/OVCM Call\");\n\t\t// 6 and 7 are priority\n\t\tif (bits[offset+6]==true) priority=2;\n\t\telse priority=0;\n\t\tif (bits[offset+7]==true) priority++;\n\t\tif (priority==0) so.append(\"/No priority\");\n\t\telse so.append(\"/Priority \"+Integer.toString(priority));\n\t\treturn so.toString();\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/VoiceData.java",
    "content": "package com.dmr;\n\nimport java.io.FileWriter;\n\npublic class VoiceData {\n\t\n\t// Handle incoming voice data\n\tpublic void handleVoice (DMRDecode tTheApp,byte[] dibit_buf)\t{\n\t\tboolean bits[]=new boolean[216];\n\t\tint vdata[]=new int[27];\n\t\t// Extract the bits\n\t\tbits=extractVoiceBits(dibit_buf);\n\t\t// Pack the bits into an int array\n\t\tvdata=packBits(bits);\n\t\t// Send the data via the sockets\n\t\ttTheApp.socketThread.sendVoiceViaSocket(vdata,tTheApp.currentChannel);\n\t}\n\t\n\t// Get the voice data bits\n\tprivate boolean[] extractVoiceBits (byte dibit_buf[])\t{\n\t\tint a,r=0;\n\t\tboolean rawData[]=new boolean[216];\n\t\t// First block\n\t\tfor (a=12;a<66;a++)\t{\n\t\t\tif (dibit_buf[a]==0)\t{\n\t\t\t\trawData[r]=false;\n\t\t\t\trawData[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==1)\t{\n\t\t\t\trawData[r]=false;\n\t\t\t\trawData[r+1]=true;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==2)\t{\n\t\t\t\trawData[r]=true;\n\t\t\t\trawData[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==3)\t{\n\t\t\t\trawData[r]=true;\n\t\t\t\trawData[r+1]=true;\n\t\t\t}\n\t\t\tr=r+2;\n\t\t}\n\t\t// Second block\n\t\tfor (a=90;a<144;a++)\t{\n\t\t\tif (dibit_buf[a]==0)\t{\n\t\t\t\trawData[r]=false;\n\t\t\t\trawData[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==1)\t{\n\t\t\t\trawData[r]=false;\n\t\t\t\trawData[r+1]=true;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==2)\t{\n\t\t\t\trawData[r]=true;\n\t\t\t\trawData[r+1]=false;\n\t\t\t}\n\t\t\telse if (dibit_buf[a]==3)\t{\n\t\t\t\trawData[r]=true;\n\t\t\t\trawData[r+1]=true;\n\t\t\t}\n\t\t\tr=r+2;\n\t\t}\n\t\treturn rawData;\n\t}\n\t\n\t// Pack the bits into an int array\n\tprivate int[] packBits(boolean bts[])\t{\n\t\tint a,c=0;\n\t\tint by[]=new int[27];\n\t\tfor (a=0;a<216;a=a+8)\t{\n\t\t\tby[c]=convertByte(bts,a);\n\t\t\tc++;\n\t\t}\n\t\treturn by;\n\t}\n\t\n\t// Get a single byte from the boolean array\n\tprivate int convertByte (boolean b[],int offset)\t{\n\t\tint dby=0;\n\t\tif (b[offset]==true) dby=127;\n\t\tif (b[offset+1]==true) dby=dby+64;\n\t\tif (b[offset+2]==true) dby=dby+32;\n\t\tif (b[offset+3]==true) dby=dby+16;\n\t\tif (b[offset+4]==true) dby=dby+8;\n\t\tif (b[offset+5]==true) dby=dby+4;\n\t\tif (b[offset+6]==true) dby=dby+2;\n\t\tif (b[offset+7]==true) dby++;\n\t\treturn dby;\n\t}\n\t\n\t// A function to save voice data to enable debugging\n\tprivate void voiceDump (int vdata[])\t{\n\t    try\t{\n\t    \tint a;\n\t    \tStringBuilder vline=new StringBuilder(500);\n\t    \tFileWriter vfile=new FileWriter(\"voice.csv\",true);\n\t    \t// Run through all 27 ints\n\t    \tfor (a=0;a<27;a++)\t{\n\t    \t\tif (a>0) vline.append(\",\");\n\t    \t\tvline.append(Integer.toString(vdata[a]));\n\t    \t}\n\t    \tvline.append(\"\\r\\n\");\n\t    \tvfile.write(vline.toString());\n\t    \tvfile.flush();  \n\t    \tvfile.close();\n\t    \t}catch (Exception e)\t{\n\t    \t\tSystem.err.println(\"Error: \" + e.getMessage());\n\t    \t\t}\n\t\t}\n\t\n\n}\n"
  },
  {
    "path": "src/main/java/com/dmr/crc.java",
    "content": "package com.dmr;\n\npublic class crc {\n\tprivate int crc8Value,crc16Value;\n\n\tpublic void setCrc8Value(int crc8Value) {\n\t\tthis.crc8Value = crc8Value;\n\t}\n\n\tpublic int getCrc8Value() {\n\t\treturn crc8Value;\n\t}\n\n\t// The CRC8 routine //\n\tpublic void crc8(boolean bit) {\n        boolean shiftBit;\n        if ((crc8Value&0x01)>0) shiftBit=true;\n        else shiftBit=false;\n        crc8Value=crc8Value>>1; \n        if ((bit^shiftBit)==true) crc8Value=crc8Value^0xe0; \n\t}\n\t\n\t// The CCITT CRC16 routine //\n\tprivate void ccitt_crc16(int in) {\n\t\tboolean c15,bit;\n\t\tbyte c=(byte)in;\n\t\tfor (int i=0;i<8;i++) {\n\t\t\tc15=((crc16Value>>15&1)== 1);\n\t\t\tbit=((c>>(7-i)&1)==1);\n\t\t\tcrc16Value<<=1;\n\t\t\tif (c15^bit) crc16Value^=0x1021;\n\t\t}\n\t\tcrc16Value=crc16Value&0xffff;\n\t}\n\t\n\t// CSBK CRC check\n\tpublic boolean crcCSBK (boolean in[])\t{\n\t\tint a,b,val;\n\t\tcrc16Value=0;\n\t\t// Run through all 96 bits\n\t\tfor (a=0;a<96;a=a+8)\t{\n\t\t\tval=0;\n\t\t\tfor (b=0;b<8;b++)\t{\n\t\t\t\tif (in[a+b]==true) val=val+(int)Math.pow(2.0,(7.0-b));\n\t\t\t}\n\t\t\t// Allow for the CSBK CRC mask\n\t\t\tif (a>=80) val=val^0xA5;\n\t\t\tccitt_crc16(val);\t\n\t\t}\n\t\tif (crc16Value==0x1D0F) return true;\n\t\telse return false;\n\t}\n\t\n\t// Data Header CRC check\n\tpublic boolean crcDataHeader (boolean in[])\t{\n\t\tint a,b,val;\n\t\tcrc16Value=0;\n\t\t// Run through all 96 bits\n\t\tfor (a=0;a<96;a=a+8)\t{\n\t\t\tval=0;\n\t\t\tfor (b=0;b<8;b++)\t{\n\t\t\t\tif (in[a+b]==true) val=val+(int)Math.pow(2.0,(7.0-b));\n\t\t\t}\n\t\t\t// Allow for the Data Header CRC mask\n\t\t\tif (a>=80) val=val^0xCC;\n\t\t\tccitt_crc16(val);\t\n\t\t}\n\t\tif (crc16Value==0x1D0F) return true;\n\t\telse return false;\n\t}\n\t\n\t// Reed-Solomon (12,9) check\n\t// TODO : Get the Reed Solomon (12,9) check routine working\n\tpublic boolean RS129 (boolean in[])\t{\n\t\tint a,b,d,byteCount=0;\n\t\tint inBytes[]=new int[12];\n\t\t// Convert from binary to an array of integers\n\t\tfor (a=0;a<96;a=a+8)\t{\n\t\t\tinBytes[byteCount]=0;\n\t\t\tfor (b=0;b<8;b++)\t{\n\t\t\t\td=(int)Math.pow(2.0,((8-b)-1));\n\t\t\t\tif (in[a+b]==true) inBytes[byteCount]=inBytes[byteCount]+d;\n\t\t\t}\n\t\t\tbyteCount++;\n\t\t}\n\t\t\n\t\t\n\t\treturn false;\n\t}\n\t\n\t// CRC 5 check\n\tpublic boolean crcFiveBit (boolean in[],int tcrc)\t{\n\t\tint a,b=0,oct=0,total=0;\n\t\t// Convert the boolean array into an array of ints\n\t\tfor (a=0;a<72;a++)\t{\n\t\t\tif (in[a]==true) oct=oct+(int)Math.pow(2.0,(int)b);\n\t\t\tb++;\n\t\t\tif (b==8)\t{\n\t\t\t\tb=0;\n\t\t\t\ttotal=total+oct;\n\t\t\t\toct=0;\n\t\t\t}\n\t\t}\n\t\ttotal=total%31;\n\t\tif (total==tcrc) return true;\n\t\telse return false;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/test/com/dmr/BPTC19696Test.java",
    "content": "package test.com.dmr;\n\nimport java.util.Arrays;\n\nimport com.dmr.BPTC19696;\n\nimport junit.framework.TestCase;\n\npublic class BPTC19696Test extends TestCase {\n\t\n\tpublic void testdecode ()\t{\n\t\tboolean ok;\n\t\tboolean idleContents[]={\n\t\t\t\ttrue,true,true,true,true,true,true,true,\n\t\t\t\ttrue,false,false,false,false,false,true,true,true,true,false,\n\t\t\t\ttrue,true,true,true,true,false,false,false,true,false,true,\n\t\t\t\ttrue,true,false,false,true,true,false,false,true,false,false,\n\t\t\t\tfalse,false,false,true,false,false,true,false,true,false,false,\n\t\t\t\ttrue,true,true,false,true,true,false,true,false,false,false,\n\t\t\t\ttrue,true,true,true,false,false,true,true,true,true,true,\n\t\t\t\tfalse,false,true,true,false,true,true,false,false,false,true,\n\t\t\t\tfalse,true,false,true,false,false,true,false,false,false,true\n\t\t};\n\t\tbyte uniFrame_good[]={3,3,2,0,2,0,3,3,1,3,0,2,1,1,0,3,3,0,0,2,1,1,3,2,2,2,2,3,2,2,2,0,1,2,1,3,0,1,3,1,3,0,1,3,0,3,2,0,0,3,2,3,3,1,2,1,0,3,1,2,0,1,1,2,1,2,3,1,3,3,3,3,1,1,1,3,3,1,1,3,1,1,3,1,3,3,1,1,3,1,3,3,1,0,3,3,3,3,1,2,3,2,1,0,1,2,1,1,0,1,1,3,0,1,2,3,1,0,2,0,3,0,2,2,1,2,3,1,1,0,3,3,3,0,1,2,0,1,0,0,2,3,1,0};\n\t\tbyte uniFrame_bad[]= {3,3,2,0,2,0,3,3,1,3,0,2,1,1,0,3,3,0,0,2,1,1,3,3,2,2,2,3,2,2,2,0,1,2,1,3,0,1,3,1,3,0,1,3,0,3,2,0,0,3,2,3,3,1,2,1,0,3,1,2,0,1,1,2,1,2,3,1,3,3,3,3,1,1,1,3,3,1,1,3,1,1,3,1,3,3,1,1,3,1,3,3,1,0,3,3,3,3,1,2,3,2,1,0,1,2,1,1,0,1,1,3,0,1,2,3,1,0,2,0,3,0,2,2,1,2,3,1,1,0,3,3,3,0,1,2,0,1,0,0,2,3,1,0};\n\t\tBPTC19696 bptc19696=new BPTC19696();\n\t\tok=bptc19696.decode(uniFrame_good);\n\t\tassertEquals(true,ok);\n\t\tboolean data[]=bptc19696.dataOut();\n\t\tok=Arrays.equals(data,idleContents);\t\n\t\tassertEquals(true,ok);\n\t\tok=bptc19696.decode(uniFrame_bad);\n\t\tassertEquals(false,ok);\n\t}\n\t\n\n}\n"
  },
  {
    "path": "src/main/java/test/com/dmr/SlotTypeTest.java",
    "content": "package test.com.dmr;\n\nimport com.dmr.SlotType;\n\nimport junit.framework.TestCase;\n\npublic class SlotTypeTest extends TestCase {\n\t\n\tpublic void testDecode ()\t{\n\t\tbyte uniFrame[]={2,0,2,0,2,0,0,2,0,0,0,0,1,1,0,3,3,0,0,2,1,1,3,2,2,2,2,3,2,2,2,0,1,2,1,3,0,1,3,1,3,0,1,3,0,3,2,0,0,3,2,3,3,1,2,1,0,3,1,2,0,1,1,2,1,2,3,1,3,3,3,3,1,1,1,3,3,1,1,3,1,1,3,1,3,3,1,1,3,1,3,3,1,0,3,3,3,3,1,2,3,2,1,0,1,2,1,1,0,1,1,3,0,1,2,3,1,0,2,0,3,0,2,2,1,2,3,1,1,0,3,3,3,0,1,2,0,1,0,0,2,3,1,0};\n\t\tSlotType slottype=new SlotType();\n\t\tString sret=slottype.decode(null, uniFrame);\n\t\tassertEquals(\"Slot Type : Colour Code 5 Idle\",sret);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/test/com/dmr/TrellisTest.java",
    "content": "package test.com.dmr;\n\nimport junit.framework.TestCase;\nimport com.dmr.Trellis;\n\npublic class TrellisTest extends TestCase {\n\n\n\tpublic void testTrellis()\t{\n\t\t\n\t\t// Three good sample 3/4 rate frames\n\t\tfinal boolean threequarterData1[]={\n\t\t\t\tfalse,false,true,false,true,true,true,false,true,false, \n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\ttrue,true,true,false,true,false,false,true,false,true,\n\t\t\t\ttrue,false,false,false,false,true,false,true,true,true,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,true,false,false,true,false,true,true,\n\t\t\t\tfalse,false,true,true,false,false,false,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,true,true,false,true,\n\t\t\t\ttrue,false,false,true,false,true,true,true,false,false,\n\t\t\t\tfalse,false,false,true,true,true,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,true,false,\n\t\t\t\ttrue,true,true,true,false,false};\n\t\tfinal boolean threequarterData2[]={\n\t\t\t\tfalse,false,true,false,true,false,true,true,false,false, \n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,true,true,true,false,true,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,true,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,true,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false};\n\t\tfinal boolean threequarterData3[]={\n\t\t\t\tfalse,false,true,false,true,false,false,false,false,true, \n\t\t\t\ttrue,false,true,true,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,true,false,false,true,true,true,\n\t\t\t\tfalse,true,false,false,true,false,true,true,false,false,\n\t\t\t\ttrue,true,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,true,true,true,true,\n\t\t\t\tfalse,false,true,false,true,true,true,true,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\ttrue,true,true,false,false,false,true,false,true,true,\n\t\t\t\ttrue,true,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,true,true,false,true,false,false,\n\t\t\t\ttrue,false,true,true,true,true,true,true,true,false,\n\t\t\t\tfalse,false,true,false,false,false,false,true,true,true,\n\t\t\t\ttrue,true,true,true,true,false,false,true,true,true,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,true,false,true,false,false,true,false,\n\t\t\t\tfalse,false,true,false,true,false,true,false,false,false,\n\t\t\t\tfalse,true,false,true,false,false,false,false,true,false,\n\t\t\t\ttrue,false,false,true,true,true,false,true,false,false,\n\t\t\t\ttrue,false,false,false,true,false};\n\t\t// and a bad frame\n\t\tfinal boolean threequarterBadData1[]={\n\t\t\t\tfalse,false,true,false,false,false,false,false,false,true, \n\t\t\t\ttrue,false,true,true,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,true,false,false,true,true,true,\n\t\t\t\tfalse,true,false,false,true,false,true,true,false,false,\n\t\t\t\ttrue,true,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,false,true,false,true,true,true,true,\n\t\t\t\tfalse,false,true,false,true,true,true,true,false,false,\n\t\t\t\ttrue,false,false,false,true,false,false,false,true,false,\n\t\t\t\ttrue,true,true,true,false,false,true,false,true,true,\n\t\t\t\ttrue,true,false,false,true,false,false,false,true,false,\n\t\t\t\tfalse,false,true,false,true,true,false,true,false,false,\n\t\t\t\ttrue,false,true,true,true,true,true,true,true,false,\n\t\t\t\tfalse,false,true,false,false,false,false,true,true,true,\n\t\t\t\ttrue,true,true,true,true,false,false,true,true,true,\n\t\t\t\tfalse,false,true,false,false,false,true,false,false,false,\n\t\t\t\ttrue,false,false,true,false,true,false,false,true,false,\n\t\t\t\tfalse,false,true,false,true,false,true,false,false,false,\n\t\t\t\tfalse,true,true,false,false,false,false,false,true,false,\n\t\t\t\ttrue,false,false,true,true,true,false,true,false,false,\n\t\t\t\ttrue,false,false,false,true,false};\n\t\t\n\t\t// Now to test the Trellis class\n\t\tTrellis trellis=new Trellis();\n\t\t// Good frame 1\n\t\tboolean tst1[]=trellis.decode(threequarterData1);\n\t\tassertNotNull(tst1);\n\t\t// Good frame 2\n\t\tboolean tst2[]=trellis.decode(threequarterData2);\n\t\tassertNotNull(tst2);\n\t\t// Good frame 3\n\t\tboolean tst3[]=trellis.decode(threequarterData3);\n\t\tassertNotNull(tst3);\n\t\t// Bad frame\n\t\tboolean tst4[]=trellis.decode(threequarterBadData1);\n\t\tassertNull(tst4);\n\t}\n\t\n\n}\n"
  },
  {
    "path": "src/main/java/test/com/dmr/crcTest.java",
    "content": "package test.com.dmr;\n\nimport com.dmr.crc;\n\nimport junit.framework.TestCase;\n\npublic class crcTest extends TestCase {\n\t\n\t// Test the CRC8 code\n\tpublic void testCRC8 ()\t{\n\t\tint a,returnCRC;\n\t\tcrc crctest=new crc();\n\t\tboolean testBinaryPass[]={true,true,true,true,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,true,false,false};\n\t\tboolean testBinaryFail[]={false,true,true,true,false,false,false,true,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,true,false,false};\n\t\t// Test for a pass\n\t\tcrctest.setCrc8Value(0);\n\t\tfor (a=0;a<testBinaryPass.length;a++)\t{\n\t\t\tcrctest.crc8(testBinaryPass[a]);\n\t\t}\n\t\treturnCRC=crctest.getCrc8Value();\n\t\tassertEquals(0,returnCRC);\n\t\t// Test for a failure\n\t\tcrctest.setCrc8Value(0);\n\t\tfor (a=0;a<testBinaryFail.length;a++)\t{\n\t\t\tcrctest.crc8(testBinaryFail[a]);\n\t\t}\n\t\treturnCRC=crctest.getCrc8Value();\n\t\tassertEquals(152,returnCRC);\n\t}\n\t\n\t// Test the CSBK CCITT CRC code\n\tpublic void testcrcCSBK ()\t{\n\t\tboolean ok1,ok2,ok3;\n\t\tcrc crctest=new crc();\n\t\tboolean testCSBK1[]={true,false,true,true,true,true,true,false,false,false,false,true,false,false,false,false,true,true,true,false,false,false,true,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true,false,false,true,true,false,true,true,false,true,true,false,false,true};\n\t\tboolean testCSBK2[]={true,false,true,true,true,true,true,false,false,false,false,true,false,false,false,false,true,true,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,true,true,true,false,false,false,false,true,true};\n\t\tboolean testCSBK3[]={true,false,true,true,true,true,true,false,false,false,false,true,false,false,false,false,true,true,false,false,false,false,true,true,false,false,false,true,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,true,true,true,false,false,false,false,false};\n\t\tok1=crctest.crcCSBK(testCSBK1);\n\t\tok2=crctest.crcCSBK(testCSBK2);\n\t\tok3=crctest.crcCSBK(testCSBK3);\n\t    assertEquals(true,ok1);\t\n\t    assertEquals(true,ok2);\n\t    assertEquals(true,ok3);\t\n\t    \n\t    \n\t}\n\t\n\t// Test the Data Header CCITT CRC code\n\tpublic void testcrcDataHeader ()\t{\n\t\tboolean ok1,ok2,ok3;\n\t\tcrc crctest=new crc();\n\t\tboolean testDH1[]={false,false,false,true,true,true,true,true,false,false,false,true,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,true,false,false,true,false,false,false,true,false,false,false,false,false,false,false,false,false,false,true,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,true,true,true,false,false,true,false,false,false,false,false,true,true,true,true,true,false,false,false,true,false,true,true,false,false,false,false,false,false,false,false};\n\t\tboolean testDH2[]={false,false,false,false,false,false,false,true,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,false,true,false,true,true,true,true,true,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,true,false,false,true,false,true,true,true,false,true,false,false};\n\t\tboolean testDH3[]={false,true,false,false,false,false,false,true,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,false,true,false,true,true,true,true,true,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,true,false,false,true,false,true,true,true,false,true,false,false};\n\t\tok1=crctest.crcDataHeader(testDH1);\n\t\tok2=crctest.crcDataHeader(testDH2);\n\t\tok3=crctest.crcDataHeader(testDH3);\n\t    assertEquals(true,ok1);\t\n\t    assertEquals(true,ok2);\n\t    assertEquals(false,ok3);\t\n\t}\n\t\n\t// Test the Reed-Solomon (12,9) code\n\tpublic void testRS129 ()\t{\n\t\tboolean ok1;\n\t\tcrc crctest=new crc();\n\t\tboolean testHeaderRS[]={false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,true,false,true,false,true,false,true,false,true,false,false,false,false,false,true,true,false,false,true,false,false,true,true,true,false,true,false};\n\t\tok1=crctest.RS129(testHeaderRS);\n\t\tassertEquals(true,ok1);\t\n\t}\n\t\n\n}\n"
  }
]