mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-10 23:57:25 -05:00
Compare commits
10 Commits
v0.5.0-alp
...
v0.5.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eaa3433034 | ||
|
|
838016c89f | ||
|
|
030a88f5a3 | ||
|
|
de3ba5529a | ||
|
|
7962758e05 | ||
|
|
0f9d1c0792 | ||
|
|
48be38a2d8 | ||
|
|
207893e5a8 | ||
|
|
e07d6fa854 | ||
|
|
e26698d0d8 |
@@ -33,7 +33,7 @@ repositories {
|
||||
maven { url "https://jitpack.io" }
|
||||
}
|
||||
|
||||
version = '0.5.0-alpha2'
|
||||
version = '0.5.0-alpha3'
|
||||
sourceCompatibility = '13'
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -83,6 +83,7 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
private SourceManager mSourceManager;
|
||||
private AliasModel mAliasModel;
|
||||
private UserPreferences mUserPreferences;
|
||||
private List<Long> mUnTunableFrequencies = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructs the channel processing manager
|
||||
@@ -185,15 +186,25 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
if(channel.getSourceConfiguration() instanceof SourceConfigTuner)
|
||||
{
|
||||
long frequency = ((SourceConfigTuner)channel.getSourceConfiguration()).getFrequency();
|
||||
mLog.error("Error starting requested channel [" + channel.getName() + ":" + frequency +
|
||||
"] - " + ce.getMessage());
|
||||
|
||||
if(!mUnTunableFrequencies.contains(frequency))
|
||||
{
|
||||
mUnTunableFrequencies.add(frequency);
|
||||
mLog.error("Error starting requested channel [" + channel.getName() + ":" + frequency +
|
||||
"] - " + ce.getMessage());
|
||||
}
|
||||
}
|
||||
else if(channel.getSourceConfiguration() instanceof SourceConfigTunerMultipleFrequency)
|
||||
{
|
||||
List<Long> frequencies = ((SourceConfigTunerMultipleFrequency)channel
|
||||
.getSourceConfiguration()).getFrequencies();
|
||||
mLog.error("Error starting requested channel [" + channel.getName() + ":" + frequencies +
|
||||
"] - " + ce.getMessage());
|
||||
|
||||
if(frequencies.size() > 0 && !mUnTunableFrequencies.contains(frequencies.get(0)))
|
||||
{
|
||||
mUnTunableFrequencies.add(frequencies.get(0));
|
||||
mLog.error("Error starting requested channel [" + channel.getName() + ":" + frequencies +
|
||||
"] - " + ce.getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -422,7 +422,7 @@ public class AliasConfigurationEditor extends SplitPane
|
||||
mAliasTableView.setItems(getAliasSortedList());
|
||||
mAliasTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||
mAliasTableView.getSelectionModel().getSelectedItems().addListener((ListChangeListener<Alias>)c -> {
|
||||
setAliases(mAliasTableView.getSelectionModel().getSelectedItems());
|
||||
Platform.runLater(() -> setAliases(mAliasTableView.getSelectionModel().getSelectedItems()));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -287,7 +287,19 @@ public class RadioReferenceDecoder
|
||||
*/
|
||||
public boolean isLSM(Site site)
|
||||
{
|
||||
return site != null && site.getModulation() != null && site.getModulation().contentEquals("LSM");
|
||||
if(site != null)
|
||||
{
|
||||
if(site.getModulation() != null && site.getModulation().contentEquals("LSM"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if(site.getDescription() != null && site.getDescription().contains("Simulcast"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -479,20 +479,19 @@ public class SiteEditor extends GridPane
|
||||
|
||||
private void createControlChannel()
|
||||
{
|
||||
SiteFrequency control = null;
|
||||
List<SiteFrequency> controls = new ArrayList<>();
|
||||
|
||||
for(SiteFrequency siteFrequency: getSiteFrequencyTableView().getItems())
|
||||
{
|
||||
if(isControl(siteFrequency) && siteFrequency.getFrequency() > 0.01)
|
||||
{
|
||||
control = siteFrequency;
|
||||
continue;
|
||||
controls.add(siteFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
if(control == null)
|
||||
if(controls.isEmpty())
|
||||
{
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Site Has No Control Channel", ButtonType.OK);
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Site Has No Control Channel(s)", ButtonType.OK);
|
||||
alert.setTitle("Create Channel Configuration");
|
||||
alert.setHeaderText("Can't Create Channel Configuration");
|
||||
alert.initOwner((getCreateChannelConfigurationButton()).getScene().getWindow());
|
||||
@@ -514,9 +513,24 @@ public class SiteEditor extends GridPane
|
||||
|
||||
channel.setDecodeConfiguration(getDecodeConfiguration(decoderType, mCurrentSite.getSite(), mCurrentSystemInformation));
|
||||
|
||||
SourceConfigTuner sourceConfigTuner = new SourceConfigTuner();
|
||||
sourceConfigTuner.setFrequency((long)(control.getFrequency() * 1E6));
|
||||
channel.setSourceConfiguration(sourceConfigTuner);
|
||||
if(controls.size() == 1)
|
||||
{
|
||||
SourceConfigTuner sourceConfigTuner = new SourceConfigTuner();
|
||||
sourceConfigTuner.setFrequency((long)(controls.get(0).getFrequency() * 1E6));
|
||||
channel.setSourceConfiguration(sourceConfigTuner);
|
||||
}
|
||||
else
|
||||
{
|
||||
SourceConfigTunerMultipleFrequency sourceConfig = new SourceConfigTunerMultipleFrequency();
|
||||
|
||||
for(SiteFrequency control: controls)
|
||||
{
|
||||
sourceConfig.addFrequency((long)(control.getFrequency() * 1E6));
|
||||
}
|
||||
|
||||
channel.setSourceConfiguration(sourceConfig);
|
||||
}
|
||||
|
||||
mPlaylistManager.getChannelModel().addChannel(channel);
|
||||
|
||||
if(getGoToChannelEditorCheckBox().isSelected())
|
||||
@@ -548,7 +562,7 @@ public class SiteEditor extends GridPane
|
||||
|
||||
if(siteFrequencies.isEmpty())
|
||||
{
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Site Has No Control or Alternate Channels", ButtonType.OK);
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Site Has No Control or Alternate Channel(s)", ButtonType.OK);
|
||||
alert.setTitle("Create Channel Configuration");
|
||||
alert.setHeaderText("Can't Create Channel Configuration");
|
||||
alert.initOwner((getCreateChannelConfigurationButton()).getScene().getWindow());
|
||||
|
||||
@@ -34,7 +34,12 @@ import org.apache.commons.io.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
/**
|
||||
* Logback and SLF4j logging implementation.
|
||||
@@ -126,6 +131,17 @@ public class ApplicationLog
|
||||
((ch.qos.logback.classic.Logger)logger).addAppender(mRollingFileAppender);
|
||||
|
||||
// StatusPrinter.print(loggerContext);
|
||||
Attributes atts = findManifestAttributes();
|
||||
if (atts != null) {
|
||||
mLog.info("SDRTrunk Version : " + atts.getValue("Implementation-Version"));
|
||||
mLog.info("Gradle Version : " + atts.getValue("Created-By"));
|
||||
mLog.info("Build Timestamp : " + atts.getValue("Build-Timestamp"));
|
||||
mLog.info("Build-JDK : " + atts.getValue("Build-JDK"));
|
||||
mLog.info("Build OS : " + atts.getValue("Build-OS"));
|
||||
}
|
||||
else{
|
||||
mLog.info("Failed to find build information.");
|
||||
}
|
||||
|
||||
mLog.info("");
|
||||
mLog.info("*******************************************************************");
|
||||
@@ -163,4 +179,25 @@ public class ApplicationLog
|
||||
mRollingFileAppender = null;
|
||||
}
|
||||
}
|
||||
|
||||
private Attributes findManifestAttributes() {
|
||||
try {
|
||||
Enumeration<URL> resources = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF");
|
||||
while (resources.hasMoreElements()) {
|
||||
try {
|
||||
Manifest manifest = new Manifest(resources.nextElement().openStream());
|
||||
Attributes atts = manifest.getMainAttributes();
|
||||
Boolean hasTitle = atts.containsValue("sdrtrunk project");
|
||||
if (hasTitle) {
|
||||
return atts;
|
||||
}
|
||||
} catch (IOException E) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
|
||||
package io.github.dsheirer.module.decode.ip;
|
||||
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Data packet interface
|
||||
*/
|
||||
@@ -28,16 +31,21 @@ public interface IPacket
|
||||
/**
|
||||
* Header for the data packet.
|
||||
*/
|
||||
public IHeader getHeader();
|
||||
IHeader getHeader();
|
||||
|
||||
/**
|
||||
* Optional payload for the packet
|
||||
* @return null if there is no payload
|
||||
*/
|
||||
public IPacket getPayload();
|
||||
IPacket getPayload();
|
||||
|
||||
/**
|
||||
* Indicates if this packet contains an optional payload
|
||||
*/
|
||||
public boolean hasPayload();
|
||||
boolean hasPayload();
|
||||
|
||||
/**
|
||||
* List of identifiers for this packet and any child payload packets.
|
||||
*/
|
||||
List<Identifier> getIdentifiers();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ package io.github.dsheirer.module.decode.ip;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.module.decode.ip.ars.ARSPacket;
|
||||
import io.github.dsheirer.module.decode.ip.cellocator.MCGPHeader;
|
||||
import io.github.dsheirer.module.decode.ip.cellocator.MCGPMessageFactory;
|
||||
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Header;
|
||||
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet;
|
||||
import io.github.dsheirer.module.decode.ip.udp.UDPPacket;
|
||||
@@ -82,15 +84,23 @@ public class PacketMessageFactory
|
||||
/**
|
||||
* Creates a UDP/IP packet payload parser
|
||||
*
|
||||
* @param sourcePort for the packet (to identify the protocol/format)
|
||||
* @param destinationPort for the packet (to identify the protocol/format)
|
||||
* @param binaryMessage containing the IP/UDP payload
|
||||
* @param offset in the message to the start of the payload
|
||||
* @return constructed packet messge parser
|
||||
*/
|
||||
public static IPacket createUDPPayload(int destinationPort, BinaryMessage binaryMessage, int offset)
|
||||
public static IPacket createUDPPayload(int sourcePort, int destinationPort, BinaryMessage binaryMessage, int offset)
|
||||
{
|
||||
switch(destinationPort)
|
||||
{
|
||||
case 231:
|
||||
//Cellocator
|
||||
if(MCGPHeader.isCellocatorMessage(binaryMessage, offset))
|
||||
{
|
||||
return MCGPMessageFactory.create(binaryMessage, offset);
|
||||
}
|
||||
break;
|
||||
case 4001: //Location Service
|
||||
break;
|
||||
case 4004: //XCMP Service
|
||||
@@ -103,6 +113,23 @@ public class PacketMessageFactory
|
||||
break;
|
||||
}
|
||||
|
||||
switch(sourcePort)
|
||||
{
|
||||
case 231:
|
||||
//Cellocator
|
||||
if(MCGPHeader.isCellocatorMessage(binaryMessage, offset))
|
||||
{
|
||||
return MCGPMessageFactory.create(binaryMessage, offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//This is normally source or destination port 231, but can be on other ports as well.
|
||||
if(MCGPHeader.isCellocatorMessage(binaryMessage, offset))
|
||||
{
|
||||
return MCGPMessageFactory.create(binaryMessage, offset);
|
||||
}
|
||||
|
||||
return new UnknownPacket(binaryMessage, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,10 @@
|
||||
package io.github.dsheirer.module.decode.ip;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPPacketHeader;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class UnknownCompressedHeaderPacket implements IPacket
|
||||
{
|
||||
@@ -87,4 +90,10 @@ public class UnknownCompressedHeaderPacket implements IPacket
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
package io.github.dsheirer.module.decode.ip;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class UnknownPacket implements IPacket
|
||||
{
|
||||
@@ -78,4 +81,10 @@ public class UnknownPacket implements IPacket
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,11 @@
|
||||
package io.github.dsheirer.module.decode.ip.ars;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ip.IPacket;
|
||||
import io.github.dsheirer.module.decode.ip.Packet;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ARSPacket extends Packet
|
||||
{
|
||||
@@ -64,4 +67,10 @@ public class ARSPacket extends Packet
|
||||
//There are no payloads for ARS packets ... the header parses all of the information.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
/**
|
||||
* Controller (Inbound) Acknowledge Packet
|
||||
*/
|
||||
public class AcknowledgeMessage extends MCGPPacket
|
||||
{
|
||||
//This field is byte reversed (ie big endian)
|
||||
private static final int[] TARGET_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] COMMAND_NUMERATOR = new int[]{32, 33, 34, 35, 36, 37, 38, 39};
|
||||
//This field is (possibly) byte reversed (ie big endian)
|
||||
private static final int[] AUTHENTICATION_CODE = new int[]{64, 65, 66, 67, 68, 69, 70, 71, 56, 57, 58, 59, 60, 61,
|
||||
62, 63, 48, 49, 50, 51, 52, 53, 54, 55, 40, 41, 42, 43, 44, 45, 46, 47};
|
||||
private static final int[] ACTION_CODE = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
|
||||
//This field is byte reversed (ie big endian)
|
||||
private static final int[] MAIN_ACKNOWLEDGE_NUMBER = new int[]{88, 89, 90, 91, 92, 93, 94, 95, 80, 81, 82, 83, 84,
|
||||
85, 86, 87};
|
||||
//This field is byte reversed (ie big endian)
|
||||
private static final int[] SECONDARY_ACKNOWLEDGE_NUMBER = new int[]{104, 105, 106, 107, 108, 109, 110, 111, 96, 97,
|
||||
98, 99, 100, 101, 102, 103};
|
||||
private static final int[] RESERVED = new int[]{112, 113, 114, 115, 116, 117, 118, 119};
|
||||
private static final int[] YEAR = new int[]{121, 122, 123, 124, 125, 126, 127};
|
||||
private static final int[] MONTH = new int[]{133, 134, 135, 120};
|
||||
private static final int[] DAY = new int[]{128, 129, 130, 131, 132};
|
||||
private static final int[] RESERVED_2 = new int[]{152, 153, 154, 155, 156, 157, 158}; //Always 0x40
|
||||
private static final int[] HOUR = new int[]{139, 140, 141, 142, 143};
|
||||
private static final int[] MINUTE = new int[]{149, 150, 151, 136, 137, 138};
|
||||
private static final int[] SECOND = new int[]{159, 144, 145, 146, 147, 148};
|
||||
|
||||
private CellocatorRadioIdentifier mTargetRadioId;
|
||||
|
||||
/**
|
||||
* Constructs a parser for a header contained within a binary message starting at the offset.
|
||||
*
|
||||
* @param header for this message
|
||||
* @param message containing the packet
|
||||
* @param offset to the packet within the message
|
||||
*/
|
||||
public AcknowledgeMessage(MCGPHeader header, BinaryMessage message, int offset)
|
||||
{
|
||||
super(header, message, offset);
|
||||
}
|
||||
|
||||
public CellocatorRadioIdentifier getRadioId()
|
||||
{
|
||||
if(mTargetRadioId == null)
|
||||
{
|
||||
mTargetRadioId = CellocatorRadioIdentifier.createTo(getMessage().getInt(TARGET_UNIT_ID, getOffset()));
|
||||
}
|
||||
|
||||
return mTargetRadioId;
|
||||
}
|
||||
|
||||
public MCGPMessageType getAcknowledgedCommand()
|
||||
{
|
||||
int command = getMessage().getInt(COMMAND_NUMERATOR, getOffset());
|
||||
return MCGPMessageType.getOutboundMessageType(command);
|
||||
}
|
||||
|
||||
public String getAuthenticationCode()
|
||||
{
|
||||
return Integer.toHexString(getMessage().getInt(AUTHENTICATION_CODE, getOffset())).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Always set to zero for an acknowledge message
|
||||
*/
|
||||
public int getActionCode()
|
||||
{
|
||||
return getMessage().getInt(ACTION_CODE, getOffset());
|
||||
}
|
||||
|
||||
public int getMainAcknowledgeNumber()
|
||||
{
|
||||
return getMessage().getInt(MAIN_ACKNOWLEDGE_NUMBER, getOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently not used and set to zero.
|
||||
*/
|
||||
public int getSecondaryAcknowledgeNumber()
|
||||
{
|
||||
return getMessage().getInt(SECONDARY_ACKNOWLEDGE_NUMBER, getOffset());
|
||||
}
|
||||
|
||||
public long getTimestamp()
|
||||
{
|
||||
Calendar calendar = new GregorianCalendar();
|
||||
calendar.clear();
|
||||
|
||||
int year = 2000 + getMessage().getInt(YEAR, getOffset());
|
||||
int month = getMessage().getInt(MONTH, getOffset()) - 1; //Calendar month is zero based
|
||||
int day = getMessage().getInt(DAY, getOffset());
|
||||
int hour = getMessage().getInt(HOUR, getOffset());
|
||||
int minute = getMessage().getInt(MINUTE, getOffset());
|
||||
int second = getMessage().getInt(SECOND, getOffset());
|
||||
|
||||
calendar.set(year, month, day, hour, minute, second);
|
||||
|
||||
return calendar.getTimeInMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CELLOCATOR RADIO:");
|
||||
sb.append(getRadioId());
|
||||
sb.append(" ACKNOWLEDGE:").append(getAcknowledgedCommand());
|
||||
sb.append(" MESSAGE NUMBER:").append(getMainAcknowledgeNumber());
|
||||
sb.append(" TIME:").append(new Date(getTimestamp()));
|
||||
sb.append(" AUTH:").append(getAuthenticationCode());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* Cellocator Radio Identifier
|
||||
*/
|
||||
public class CellocatorRadioIdentifier extends RadioIdentifier
|
||||
{
|
||||
public CellocatorRadioIdentifier(Integer value, Role role)
|
||||
{
|
||||
super(value, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return Protocol.CELLOCATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TO radio identifier
|
||||
*/
|
||||
public static CellocatorRadioIdentifier createTo(int radioId)
|
||||
{
|
||||
return new CellocatorRadioIdentifier(radioId, Role.TO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a FROM radio identifier
|
||||
*/
|
||||
public static CellocatorRadioIdentifier createFrom(int radioId)
|
||||
{
|
||||
return new CellocatorRadioIdentifier(radioId, Role.FROM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ANY radio identifier
|
||||
*/
|
||||
public static CellocatorRadioIdentifier createAny(int radioId)
|
||||
{
|
||||
return new CellocatorRadioIdentifier(radioId, Role.ANY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Cellocator - Commands Enumeration
|
||||
*/
|
||||
public enum Command
|
||||
{
|
||||
IMMEDIATE_STATUS_REQUEST(0x0, "IMMEDIATE STATUS AND LOCATION"),
|
||||
UNIT_STATUS_CHANGE(0x2, "CHANGE UNIT STATUS"),
|
||||
OUTPUT_STATE_CHANGE(0x3, "CHANGE OUTPUT STATE"),
|
||||
DISABLE_ACTIVE_TRANSMISSIONS(0x4, "DISABLE ACTIVE TRANSMISSIONS"),
|
||||
TRACKING_CONTROL(0x5, "TRACKING CONTROL"),
|
||||
ALARM_CADENCE_CONTROL(0x6, "ALARM CADENCE CONTROL"),
|
||||
COMMENCE_GRADUAL_ENGINE_STOP(0x7, "COMMENCE GRADUAL ENGINE STOP"),
|
||||
INITIATE_CSD_SESSION(0xC, "INITIATE CSD SESSION"),
|
||||
ERASE_TRACKING_LOG(0xD, "ERASE TRACKING LOG"),
|
||||
RESET_GPS_RECEIVER(0xE, "RESET GPS RECEIVER"),
|
||||
SEQUENCE_DETECTION_LEARN(0xF, "SEQUENCE DETECTION LEARN"),
|
||||
FORCE_GPS_ENERGIZING(0x10, "FORCE GPS ACTIVATION"),
|
||||
CONNECT_TO_SERVER(0x12, "CONNECT TO SERVER"),
|
||||
MANUFACTURER_RESERVED(0x13, "MANUFACTURER RESERVED"),
|
||||
CALIBRATE_FREQUENCY_COUNTERS(0x14, "CALIBRATE FREQUENCY COUNTERS"),
|
||||
CONTROL_COM_PORT_TRANSPARENT_MODE(0x15, "CONTROL COM PORT TRANSPARENT MODE"),
|
||||
QUERY_CONNECTED_TRAILER_ID(0x16, "QUERY CONNECTED TRAILER ID"),
|
||||
CAMERA_SUPPORT(0x18, "CAMERA SUPPORT"),
|
||||
NANO_WAKEUP_AND_STATUS_REQUEST(0x19, "NANO WAKEUP AND STATUS REQUEST"),
|
||||
ENABLE_MODEM_FOTA_SESSION(0x1A, "ENABLE MODEM FOTA SESSION"),
|
||||
PHSN_CONTROL(0x1B, "PHSN CONTROL"),
|
||||
CALL_CONTROL(0x1C, "CALL SHARING 2 CONTROL"),
|
||||
CELLO_TRACK_T(0x1D, "CELLO TRACK T"),
|
||||
CALIBRATE_CAN_GPS_SPEED(0x1F, "CALIBRATE CAN GPS SPEED"),
|
||||
|
||||
UNKNOWN(-1, "UNKNOWN COMMAND");
|
||||
|
||||
private int mValue;
|
||||
private String mLabel;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param value of the command
|
||||
* @param label for pretty printing
|
||||
*/
|
||||
Command(int value, String label)
|
||||
{
|
||||
mValue = value;
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value of the command
|
||||
*/
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
private static Map<Integer,Command> LOOKUP_MAP = new TreeMap<>();
|
||||
|
||||
static
|
||||
{
|
||||
for(Command command: Command.values())
|
||||
{
|
||||
LOOKUP_MAP.put(command.getValue(), command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a command from its value
|
||||
* @param value of command to lookup
|
||||
* @return command or UNKNOWN
|
||||
*/
|
||||
public static Command fromValue(int value)
|
||||
{
|
||||
if(LOOKUP_MAP.containsKey(value))
|
||||
{
|
||||
return LOOKUP_MAP.get(value);
|
||||
}
|
||||
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
/**
|
||||
* Communication Control Field parser
|
||||
*/
|
||||
public class CommunicationControl
|
||||
{
|
||||
public enum Initiative {ACTIVE, RESPONSE};
|
||||
public enum SpeedType {MOMENTARY, MAX};
|
||||
|
||||
private static final int MASK_CAN_ORIGINATED_ODOMETER = 0x8000;
|
||||
private static final int MASK_CAN_ORIGINATED_SPEED = 0x4000;
|
||||
private static final int MASK_MULTI_PURPOSE_FIELD_CONTENT = 0x3000;
|
||||
private static final int MASK_CR200_CR300_MESSAGE_SOURCE = 0x800;
|
||||
private static final int MASK_GARMIN_CONNECTED = 0x400;
|
||||
private static final int MASK_GARMIN_ENABLED = 0x200;
|
||||
private static final int MASK_CR200_CR300_MESSAGE_INITIATIVE = 0x100;
|
||||
private static final int MASK_CR200_CR300_GSM_HIBERNATION_INDICATOR = 0x80;
|
||||
private static final int MASK_MOMENTARY_OR_MAX_SPEED_VALUE = 0x40;
|
||||
private static final int MASK_BUSINESS_OR_PRIVATE_MODE = 0x20;
|
||||
private static final int MASK_FIRMWARE_SUB_VERSION = 0x1F;
|
||||
|
||||
private int mValue;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param value of the 16-bit field
|
||||
*/
|
||||
public CommunicationControl(int value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the odometer value originated from the vehicle CAN bus
|
||||
*/
|
||||
public boolean isCANOriginatedOdometer()
|
||||
{
|
||||
return isSet(MASK_CAN_ORIGINATED_ODOMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the speed value originated from the vehicle CAN bus
|
||||
*/
|
||||
public boolean isCANOriginatedSpeed()
|
||||
{
|
||||
return isSet(MASK_CAN_ORIGINATED_SPEED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the Garmin GPS is connected to the unit
|
||||
*/
|
||||
public boolean isGarminConnected()
|
||||
{
|
||||
return isSet(MASK_GARMIN_CONNECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the Garmin GPS is enabled
|
||||
*/
|
||||
public boolean isGarminEnabled()
|
||||
{
|
||||
return isSet(MASK_GARMIN_ENABLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the message was initiated by the unit or if the message is a response to a request.
|
||||
*/
|
||||
public Initiative getMessageSource()
|
||||
{
|
||||
return isSet(MASK_CR200_CR300_MESSAGE_INITIATIVE) ? Initiative.RESPONSE : Initiative.ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the context of the speed value contained in the message, momentary or max recorded from last event.
|
||||
*/
|
||||
public SpeedType getSpeedType()
|
||||
{
|
||||
return isSet(MASK_MOMENTARY_OR_MAX_SPEED_VALUE) ? SpeedType.MAX : SpeedType.MOMENTARY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the masked value is set for this field
|
||||
* @param mask to check
|
||||
* @return true if all set bits of the mask value are also set in the field bits
|
||||
*/
|
||||
private boolean isSet(int mask)
|
||||
{
|
||||
return (mValue & mask) == mask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
|
||||
/**
|
||||
* Unit (Outbound) data being forwarded over the wireless interface to a central application
|
||||
*/
|
||||
public class ForwardedDataMessage extends MCGPPacket
|
||||
{
|
||||
//This field is byte reversed (ie big endian)
|
||||
private static final int[] TARGET_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] COMMAND_NUMERATOR = new int[]{48, 49, 50, 51, 52, 53, 54, 55};
|
||||
private static final int[] FORWARDED_MESSAGE_NUMBER = new int[]{88, 89, 90, 91, 92, 93, 94, 95};
|
||||
private static final int[] FRAGMENT_CONTROL = new int[]{96, 97, 98, 99, 100, 101, 102, 103};
|
||||
private static final int[] PAYLOAD_LENGTH = new int[]{112, 113, 114, 115, 116, 117, 118, 119, 104, 105, 106, 107,
|
||||
108, 109, 110, 111};
|
||||
private static final int PAYLOAD_START = 120;
|
||||
|
||||
private CellocatorRadioIdentifier mTargetRadioId;
|
||||
private FragmentControl mFragmentControl;
|
||||
|
||||
/**
|
||||
* Constructs a parser for a header contained within a binary message starting at the offset.
|
||||
*
|
||||
* @param header for this message
|
||||
* @param message containing the packet
|
||||
* @param offset to the packet within the message
|
||||
*/
|
||||
public ForwardedDataMessage(MCGPHeader header, BinaryMessage message, int offset)
|
||||
{
|
||||
super(header, message, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Target radio ID to receive the forwarded data
|
||||
*/
|
||||
public CellocatorRadioIdentifier getRadioId()
|
||||
{
|
||||
if(mTargetRadioId == null)
|
||||
{
|
||||
mTargetRadioId = CellocatorRadioIdentifier.createTo(getMessage().getInt(TARGET_UNIT_ID, getOffset()));
|
||||
}
|
||||
|
||||
return mTargetRadioId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Message fragment control structure
|
||||
*/
|
||||
public FragmentControl getFragmentControl()
|
||||
{
|
||||
if(mFragmentControl == null)
|
||||
{
|
||||
mFragmentControl = new FragmentControl(getMessage().getInt(FRAGMENT_CONTROL, getOffset()));
|
||||
}
|
||||
|
||||
return mFragmentControl;
|
||||
}
|
||||
|
||||
/**
|
||||
* This message number
|
||||
*/
|
||||
public int getMessageNumerator()
|
||||
{
|
||||
return getMessage().getInt(COMMAND_NUMERATOR, getOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Message number for the forwarded message
|
||||
*/
|
||||
public int getForwardedMessageNumerator()
|
||||
{
|
||||
return getMessage().getInt(FORWARDED_MESSAGE_NUMBER, getOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Length of the payload in bytes
|
||||
*/
|
||||
private int getPayloadLength()
|
||||
{
|
||||
return getMessage().getInt(PAYLOAD_LENGTH, getOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Hexadecimal representation of the payload data.
|
||||
*/
|
||||
public String getPayloadData()
|
||||
{
|
||||
int payloadEnd = PAYLOAD_START + (getPayloadLength() * 8);
|
||||
return getMessage().get(PAYLOAD_START, payloadEnd).toHexString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CELLOCATOR TO RADIO:");
|
||||
sb.append(getRadioId());
|
||||
sb.append(" MESSAGE NUMBER:").append(getMessageNumerator());
|
||||
|
||||
if(getHeader().getMessageType() == MCGPMessageType.OUTBOUND_FORWARDED_REALTIME_DATA)
|
||||
{
|
||||
sb.append(" FORWARDED SERIAL PORT DATA");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(" FORWARDED LOGGED DATA");
|
||||
}
|
||||
|
||||
sb.append(" MESSAGE NUMBER:").append(getForwardedMessageNumerator());
|
||||
sb.append(" ").append(getFragmentControl());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
/**
|
||||
* Provides fragment information for messages that are too long and have to be fragmented.
|
||||
*/
|
||||
public class FragmentControl
|
||||
{
|
||||
public enum Origination{UNIT,APPLICATION};
|
||||
private static final int BIT_MASK_ORIGINATION = 0x80;
|
||||
private static final int BIT_MASK_LAST_FRAGMENT = 0x40;
|
||||
private static final int BIT_MASK_FRAGMENT_INDEX = 0x3F;
|
||||
private int mValue;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param value of the fragment control field byte
|
||||
*/
|
||||
public FragmentControl(int value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getOrigination().name());
|
||||
sb.append(" ORIGINATED FRAGMENT #").append(getFragmentIndex());
|
||||
|
||||
if(isLastFragment())
|
||||
{
|
||||
sb.append(" FINAL");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the origination of the message fragment
|
||||
* @return origination, unit or application
|
||||
*/
|
||||
public Origination getOrigination()
|
||||
{
|
||||
return isSet(BIT_MASK_ORIGINATION) ? Origination.UNIT : Origination.APPLICATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this is the last fragment
|
||||
*/
|
||||
public boolean isLastFragment()
|
||||
{
|
||||
return isSet(BIT_MASK_LAST_FRAGMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragment index for this fragment, 0-31
|
||||
*/
|
||||
public int getFragmentIndex()
|
||||
{
|
||||
return (mValue & BIT_MASK_FRAGMENT_INDEX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the bits that are set in the bit mask are also set in the value field
|
||||
*/
|
||||
private boolean isSet(int bitMask)
|
||||
{
|
||||
return (mValue & bitMask) == bitMask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
|
||||
/**
|
||||
* Controller (Inbound) Generic Command
|
||||
*/
|
||||
public class GenericCommandMessage extends MCGPPacket
|
||||
{
|
||||
//This field is byte reversed (ie big endian)
|
||||
private static final int[] TARGET_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] COMMAND_NUMERATOR = new int[]{32, 33, 34, 35, 36, 37, 38, 39};
|
||||
//This field is (possibly) byte reversed (ie big endian)
|
||||
private static final int[] AUTHENTICATION_CODE = new int[]{64, 65, 66, 67, 68, 69, 70, 71, 56, 57, 58, 59, 60, 61,
|
||||
62, 63, 48, 49, 50, 51, 52, 53, 54, 55, 40, 41, 42, 43, 44, 45, 46, 47};
|
||||
private static final int[] COMMAND_CODE = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
|
||||
|
||||
private CellocatorRadioIdentifier mTargetRadioId;
|
||||
|
||||
/**
|
||||
* Constructs a parser for a header contained within a binary message starting at the offset.
|
||||
*
|
||||
* @param header for this message
|
||||
* @param message containing the packet
|
||||
* @param offset to the packet within the message
|
||||
*/
|
||||
public GenericCommandMessage(MCGPHeader header, BinaryMessage message, int offset)
|
||||
{
|
||||
super(header, message, offset);
|
||||
}
|
||||
|
||||
public CellocatorRadioIdentifier getRadioId()
|
||||
{
|
||||
if(mTargetRadioId == null)
|
||||
{
|
||||
mTargetRadioId = CellocatorRadioIdentifier.createTo(getMessage().getInt(TARGET_UNIT_ID, getOffset()));
|
||||
}
|
||||
|
||||
return mTargetRadioId;
|
||||
}
|
||||
|
||||
public int getCommandNumerator()
|
||||
{
|
||||
return getMessage().getInt(COMMAND_NUMERATOR, getOffset());
|
||||
}
|
||||
|
||||
public String getAuthenticationCode()
|
||||
{
|
||||
return Integer.toHexString(getMessage().getInt(AUTHENTICATION_CODE, getOffset())).toUpperCase();
|
||||
}
|
||||
|
||||
public int getCommandCode()
|
||||
{
|
||||
return getMessage().getInt(COMMAND_CODE, getOffset());
|
||||
}
|
||||
|
||||
public Command getCommand()
|
||||
{
|
||||
return Command.fromValue(getCommandCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CELLOCATOR RADIO:");
|
||||
sb.append(getRadioId());
|
||||
sb.append(" REQUEST:").append(getCommand());
|
||||
sb.append(" SEQUENCE:").append(getCommandNumerator());
|
||||
sb.append(" AUTH:").append(getAuthenticationCode());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Unit (Outbound) Status and Location
|
||||
*/
|
||||
public class LocationStatusMessage extends MCGPPacket
|
||||
{
|
||||
private static final int[] SOURCE_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] COMMUNICATION_CONTROL = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
|
||||
45, 46, 47};
|
||||
private static final int[] MESSAGE_NUMERATOR = new int[]{48, 49, 50, 51, 52, 53, 54, 55};
|
||||
private static final int[] HARDWARE_VERSION = new int[]{56, 57, 58, 59, 60, 61, 62, 63};
|
||||
private static final int[] SOFTWARE_VERSION = new int[]{64, 65, 66, 67, 68, 69, 70, 71};
|
||||
private static final int[] PROTOCOL_VERSION = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
|
||||
private static final int[] UNIT_STATUS = new int[]{80, 81, 82, 83};
|
||||
private static final int[] CURRENT_GSM_OPERATOR = new int[]{84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 152,
|
||||
153, 154, 155, 156, 157, 158, 159};
|
||||
private static final int[] TRANSMISSION_REASON_SPECIFIC_DATA = new int[]{96, 97, 98, 99, 100, 101, 102, 103};
|
||||
private static final int[] TRANSMISSION_REASON = new int[]{104, 105, 106, 107, 108, 109, 110, 111};
|
||||
private static final int[] UNIT_MODE_OF_OPERATION = new int[]{112, 113, 114, 115, 116, 117, 118, 119}; //Byte 20
|
||||
private static final int[] UNIT_IO_STATUS = new int[]{120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
|
||||
132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151};
|
||||
private static final int[] ANALOG_INPUT_1 = new int[]{160, 161, 162, 163, 164, 165, 166, 167};
|
||||
private static final int[] ANALOG_INPUT_2 = new int[]{168, 169, 170, 171, 172, 173, 174, 175};
|
||||
private static final int[] ANALOG_INPUT_3 = new int[]{176, 177, 178, 179, 180, 181, 182, 183};
|
||||
private static final int[] ANALOG_INPUT_4 = new int[]{184, 185, 186, 187, 188, 189, 190, 191};
|
||||
private static final int[] MILEAGE_COUNTER = new int[]{192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203,
|
||||
204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215};
|
||||
private static final int[] MULTI_PURPOSE_FIELD = new int[]{216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
|
||||
227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248,
|
||||
249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263};
|
||||
private static final int[] LAST_GPS_FIX = new int[]{264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276,
|
||||
277, 278, 279};
|
||||
private static final int[] LOCATION_STATUS = new int[]{280, 281, 282, 283, 284, 285, 286, 287};
|
||||
private static final int[] GPS_MODE_1 = new int[]{288, 289, 290, 291, 292, 293, 294, 295};
|
||||
private static final int[] GPS_MODE_2 = new int[]{296, 297, 298, 299, 300, 301, 302, 303};
|
||||
private static final int[] GPS_SATELLITES_USED = new int[]{304, 305, 306, 307, 308, 309, 310, 311};
|
||||
|
||||
//Original signed 32-bit big endian field rearranged in little endian byte format
|
||||
private static final int[] LONGITUDE = new int[]{336, 337, 338, 339, 340, 341, 342, 343, 328, 329, 330, 331, 332,
|
||||
333, 334, 335, 320, 321, 322, 323, 324, 325, 326, 327, 312, 313, 314, 315, 316, 317, 318, 319};
|
||||
|
||||
//Original signed 32-bit big endian field rearranged in little endian byte format
|
||||
private static final int[] LATITUDE = new int[]{368, 369, 370, 371, 372, 373, 374, 375, 360, 361, 362, 363, 364,
|
||||
365, 366, 367, 352, 353, 354, 355, 356, 357, 358, 359, 344, 345, 346, 347, 348, 349, 350, 351};
|
||||
|
||||
//Original signed 32-bit big endian field rearranged in little endian byte format
|
||||
private static final int[] ALTITUDE = new int[]{400, 401, 402, 403, 404, 405, 406, 407, 392, 393, 394, 395, 396,
|
||||
397, 398, 399, 384, 385, 386, 387, 388, 389, 390, 391, 376, 377, 378, 379, 380, 381, 382, 383};
|
||||
|
||||
//Original signed 32-bit big endian field rearranged in little endian byte format
|
||||
private static final int[] GROUND_SPEED = new int[]{432, 433, 434, 435, 436, 437, 438, 439, 424, 425, 426, 427, 428,
|
||||
429, 430, 431, 416, 417, 418, 419, 420, 421, 422, 423, 408, 409, 410, 411, 412, 413, 414, 415};
|
||||
|
||||
//Original signed 32-bit big endian field rearranged in little endian byte format
|
||||
private static final int[] HEADING_TRUE = new int[]{448, 449, 450, 451, 452, 453, 454, 455, 440, 441, 442, 443,
|
||||
444, 445, 446, 447};
|
||||
|
||||
private static final int[] UTC_TIME_SECOND = new int[]{456, 457, 458, 459, 460, 461, 462, 463};
|
||||
private static final int[] UTC_TIME_MINUTE = new int[]{464, 465, 466, 467, 468, 469, 470, 471};
|
||||
private static final int[] UTC_TIME_HOUR = new int[]{472, 473, 474, 475, 476, 477, 478, 479};
|
||||
private static final int[] UTC_TIME_DAY = new int[]{480, 481, 482, 483, 484, 485, 486, 487};
|
||||
private static final int[] UTC_TIME_MONTH = new int[]{488, 489, 490, 491, 492, 493, 494, 495};
|
||||
private static final int[] UTC_TIME_YEAR = new int[]{496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507,
|
||||
508, 509, 510, 511};
|
||||
|
||||
private CellocatorRadioIdentifier mSourceRadioId;
|
||||
private CommunicationControl mCommunicationControl;
|
||||
private Long mUTCTimestamp;
|
||||
|
||||
/**
|
||||
* Constructs a parser for a header contained within a binary message starting at the offset.
|
||||
*
|
||||
* @param header for this message
|
||||
* @param message containing the packet
|
||||
* @param offset to the packet within the message
|
||||
*/
|
||||
public LocationStatusMessage(MCGPHeader header, BinaryMessage message, int offset)
|
||||
{
|
||||
super(header, message, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Source radio or unit ID
|
||||
*/
|
||||
public CellocatorRadioIdentifier getRadioId()
|
||||
{
|
||||
if(mSourceRadioId == null)
|
||||
{
|
||||
mSourceRadioId = CellocatorRadioIdentifier.createFrom(getMessage().getInt(SOURCE_UNIT_ID, getOffset()));
|
||||
}
|
||||
|
||||
return mSourceRadioId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Communication control bitmap field parser.
|
||||
*/
|
||||
public CommunicationControl getCommunicationControl()
|
||||
{
|
||||
if(mCommunicationControl == null)
|
||||
{
|
||||
mCommunicationControl = new CommunicationControl(getMessage().getInt(COMMUNICATION_CONTROL, getOffset()));
|
||||
}
|
||||
|
||||
return mCommunicationControl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Message number. This is a one-up sequence maintained by the source unit that is reset to zero after
|
||||
* each power cycle and used to track the message through receipt.
|
||||
*/
|
||||
public int getMessageNumerator()
|
||||
{
|
||||
return getMessage().getInt(MESSAGE_NUMERATOR, getOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of satellites used by the GPS for calculating the position/fix.
|
||||
*/
|
||||
public int getSatelliteCount()
|
||||
{
|
||||
return getMessage().getInt(GPS_SATELLITES_USED, getOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Latitude in degrees decimal format for WGS-84 datum
|
||||
*/
|
||||
public double getLatitude()
|
||||
{
|
||||
int value = getMessage().getInt(LATITUDE, getOffset());
|
||||
double radians = (double)value / 1E8d;
|
||||
return FastMath.toDegrees(radians);
|
||||
}
|
||||
|
||||
/**
|
||||
* Longitude in degrees decimal format for WGS-84 datum
|
||||
*/
|
||||
public double getLongitude()
|
||||
{
|
||||
int value = getMessage().getInt(LONGITUDE, getOffset());
|
||||
double radians = (double)value / 1E8d;
|
||||
return FastMath.toDegrees(radians);
|
||||
}
|
||||
|
||||
/**
|
||||
* Altitude in meters
|
||||
*/
|
||||
public double getAltitude()
|
||||
{
|
||||
int centimeters = getMessage().getInt(ALTITUDE, getOffset());
|
||||
return (double)centimeters / 1E2d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Speed over ground in kph
|
||||
*/
|
||||
public double getSpeed()
|
||||
{
|
||||
int centimetersPerSecond = getMessage().getInt(GROUND_SPEED, getOffset());
|
||||
double kilometersPerSecond = (double)centimetersPerSecond / 1E5d;
|
||||
return kilometersPerSecond * 3600;
|
||||
}
|
||||
|
||||
/**
|
||||
* Heading relative to true North, 0-360 degrees.
|
||||
*/
|
||||
public double getHeading()
|
||||
{
|
||||
int headingThousandthsRadian = getMessage().getInt(HEADING_TRUE, getOffset());
|
||||
double radians = (double)headingThousandthsRadian / 1E3d;
|
||||
return FastMath.toDegrees(radians);
|
||||
}
|
||||
|
||||
/**
|
||||
* UTC timestamp for the location and status
|
||||
* @return timestamp in milliseconds since epoch
|
||||
*/
|
||||
public long getUTCTimestamp()
|
||||
{
|
||||
if(mUTCTimestamp == null)
|
||||
{
|
||||
int year = getMessage().getInt(UTC_TIME_YEAR, getOffset());
|
||||
int month = getMessage().getInt(UTC_TIME_MONTH, getOffset());
|
||||
int day = getMessage().getInt(UTC_TIME_DAY, getOffset());
|
||||
int hour = getMessage().getInt(UTC_TIME_HOUR, getOffset());
|
||||
int minute = getMessage().getInt(UTC_TIME_MINUTE, getOffset());
|
||||
int second = getMessage().getInt(UTC_TIME_SECOND, getOffset());
|
||||
|
||||
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
calendar.clear();
|
||||
calendar.set(year, month, day, hour, minute, second);
|
||||
mUTCTimestamp = calendar.getTimeInMillis();
|
||||
}
|
||||
|
||||
return mUTCTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CELLOCATOR RADIO:").append(getRadioId());
|
||||
sb.append(" GPS LOCATION: ").append(getLatitude()).append(" ").append(getLongitude());
|
||||
sb.append(" HEADING:").append(getHeading());
|
||||
sb.append(" SPEED:").append(getSpeed()).append("kph");
|
||||
sb.append(" AT:").append(new Date(getUTCTimestamp()));
|
||||
sb.append(" MESSAGE #").append(getMessageNumerator());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.module.decode.ip.Header;
|
||||
|
||||
/**
|
||||
* Cellocator MCGP Header
|
||||
*/
|
||||
public class MCGPHeader extends Header
|
||||
{
|
||||
private static final int[] M_CHARACTER = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] C_CHARACTER = {8, 9, 10, 11, 12, 13, 14, 15};
|
||||
private static final int[] G_CHARACTER = {16, 17, 18, 19, 20, 21, 22, 23};
|
||||
private static final int[] P_CHARACTER = {24, 25, 26, 27, 28, 29, 30, 31};
|
||||
private static final int M_VALUE = 0x4D;
|
||||
private static final int C_VALUE = 0x43;
|
||||
private static final int G_VALUE = 0x47;
|
||||
private static final int P_VALUE = 0x50;
|
||||
|
||||
private static final int[] MESSAGE_TYPE = {32, 33, 34, 35, 36, 37, 38, 39};
|
||||
public static final int HEADER_LENGTH = 40;
|
||||
|
||||
private MCGPMessageType mMessageType;
|
||||
|
||||
/**
|
||||
* Constructs a parser for a header contained within a binary message starting at the offset.
|
||||
*
|
||||
* @param message containing the header
|
||||
* @param offset to the header within the message
|
||||
*/
|
||||
public MCGPHeader(BinaryMessage message, int offset)
|
||||
{
|
||||
super(message, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs simple check of the first 32 bits of the message looking for the
|
||||
* signature 'MCGP' header for cellocator messages.
|
||||
* @param message to inspect
|
||||
* @param offset to the cellocator payload
|
||||
* @return true if the payload starts with MCGP
|
||||
*/
|
||||
public static boolean isCellocatorMessage(BinaryMessage message, int offset)
|
||||
{
|
||||
return message.getInt(M_CHARACTER, offset) == M_VALUE &&
|
||||
message.getInt(C_CHARACTER, offset) == C_VALUE &&
|
||||
message.getInt(G_CHARACTER, offset) == G_VALUE &&
|
||||
message.getInt(P_CHARACTER, offset) == P_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength()
|
||||
{
|
||||
return HEADER_LENGTH;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CELLOCATOR ").append(getMessageType());
|
||||
|
||||
if(getMessageType() == MCGPMessageType.UNKNOWN)
|
||||
{
|
||||
sb.append(" PACKET:").append(getMessage().toHexString());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the type of message
|
||||
*/
|
||||
public MCGPMessageType getMessageType()
|
||||
{
|
||||
if(mMessageType == null)
|
||||
{
|
||||
mMessageType = getMessageType(getMessage(), getOffset());
|
||||
}
|
||||
|
||||
return mMessageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets/Overrides the message type
|
||||
*/
|
||||
void setMessageType(MCGPMessageType type)
|
||||
{
|
||||
mMessageType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the type of MCGP message contained in the message starting at the offset index.
|
||||
*/
|
||||
public static MCGPMessageType getMessageType(BinaryMessage message, int offset)
|
||||
{
|
||||
int payloadLengthBytes = (message.size() - offset) / 8;
|
||||
return MCGPMessageType.fromValue(message.getInt(MESSAGE_TYPE, offset), payloadLengthBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Numeric value for the message type for this header
|
||||
*/
|
||||
public int getMessageTypeValue()
|
||||
{
|
||||
return getMessage().getInt(MESSAGE_TYPE, getOffset());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
|
||||
/**
|
||||
* Creates an MCGP packet and header for parsing the contents of the packet.
|
||||
*/
|
||||
public class MCGPMessageFactory
|
||||
{
|
||||
/**
|
||||
* Creates an MCGP packet message from the binary message at the specified binary message offset.
|
||||
* @param message containing transmitted bits
|
||||
* @param offset into the message for the start of the MCGP payload
|
||||
* @return MCGP packet message parser
|
||||
*/
|
||||
public static MCGPPacket create(BinaryMessage message, int offset)
|
||||
{
|
||||
MCGPHeader header = new MCGPHeader(message, offset);
|
||||
int payloadOffset = offset + MCGPHeader.HEADER_LENGTH;
|
||||
|
||||
switch(header.getMessageType())
|
||||
{
|
||||
case INBOUND_GENERIC_COMMAND:
|
||||
return new GenericCommandMessage(header, message, payloadOffset);
|
||||
case INBOUND_GENERAL_ACKNOWLEDGE:
|
||||
return new AcknowledgeMessage(header, message, payloadOffset);
|
||||
case INBOUND_PROGRAMMING_COMMAND:
|
||||
return new ProgrammingCommandMessage(header, message, payloadOffset);
|
||||
case OUTBOUND_LOCATION_STATUS:
|
||||
return new LocationStatusMessage(header, message, payloadOffset);
|
||||
case OUTBOUND_PROGRAMMING_STATUS:
|
||||
return new ProgrammingStatusMessage(header, message, payloadOffset);
|
||||
case OUTBOUND_FORWARDED_REALTIME_DATA:
|
||||
case OUTBOUND_FORWARDED_LOGGED_DATA_FRAGMENT:
|
||||
return new ForwardedDataMessage(header, message, payloadOffset);
|
||||
|
||||
case INBOUND_MODULAR_REQUEST:
|
||||
case OUTBOUND_MODULAR_RESPONSE:
|
||||
//Both messages are variable length and use message type 9 so we have to inspect the total message
|
||||
//length field for the response message which should be 0 for the request variant and non-zero for
|
||||
//the response variant.
|
||||
if(ModularResponseMessage.getTotalMessageLength(message, payloadOffset) > 0)
|
||||
{
|
||||
header.setMessageType(MCGPMessageType.OUTBOUND_MODULAR_RESPONSE);
|
||||
return new ModularResponseMessage(header, message, payloadOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ModularRequestMessage(header, message, payloadOffset);
|
||||
}
|
||||
|
||||
case UNKNOWN:
|
||||
default:
|
||||
return new UnknownMCGPMessage(header, message, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Cellocator MCGP Message Types enumeration with indicated message lengths.
|
||||
*
|
||||
* Note: message length of -1 indicates a variable length message.
|
||||
*/
|
||||
public enum MCGPMessageType
|
||||
{
|
||||
INBOUND_GENERIC_COMMAND(0, 25 * 8, "COMMAND"),
|
||||
INBOUND_PROGRAMMING_COMMAND(1, 34 * 8, "PROGRAMMING COMMAND"),
|
||||
INBOUND_GENERAL_ACKNOWLEDGE(4, 28 * 8, "ACKNOWLEDGE"),
|
||||
INBOUND_FORWARD_DATA_COMMAND(5, -1, "FORWARD DATA COMMAND"),
|
||||
INBOUND_MODULAR_REQUEST(9, -1, "MODULAR MESSAGE REQUEST"),
|
||||
|
||||
OUTBOUND_LOCATION_STATUS(0, 70 * 8, "LOCATION AND STATUS"),
|
||||
OUTBOUND_PROGRAMMING_STATUS(3, 31 * 8, "CONFIGURATION"),
|
||||
OUTBOUND_FORWARDED_LOGGED_DATA_FRAGMENT(7, -1, "FORWARDED LOGGED DATA"),
|
||||
OUTBOUND_FORWARDED_REALTIME_DATA(8, -1, "FORWARDED SERIAL_PORT DATA"),
|
||||
OUTBOUND_MODULAR_RESPONSE(9, -1, "MODULAR MESSAGE RESPONSE"),
|
||||
OUTBOUND_FIRMWARE_UPDATE(10,-1, "FIRMWARE UPDATE"),
|
||||
|
||||
UNKNOWN(-1, -1, "UNKNOWN");
|
||||
|
||||
private int mValue;
|
||||
private int mLength;
|
||||
private String mLabel;
|
||||
|
||||
MCGPMessageType(int value, int length, String label)
|
||||
{
|
||||
mValue = value;
|
||||
mLength = length;
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public static EnumSet<MCGPMessageType> INBOUND = EnumSet.range(INBOUND_GENERIC_COMMAND, INBOUND_MODULAR_REQUEST);
|
||||
public static EnumSet<MCGPMessageType> OUTBOUND = EnumSet.range(OUTBOUND_LOCATION_STATUS, OUTBOUND_FIRMWARE_UPDATE);
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public int getLength()
|
||||
{
|
||||
return mLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to lookup an entry from a value.
|
||||
*/
|
||||
public static MCGPMessageType fromValue(int value, int messageByteLength)
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case 0:
|
||||
if(messageByteLength == 25)
|
||||
{
|
||||
return INBOUND_GENERIC_COMMAND;
|
||||
}
|
||||
else if(messageByteLength == 70)
|
||||
{
|
||||
return OUTBOUND_LOCATION_STATUS;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
return INBOUND_PROGRAMMING_COMMAND;
|
||||
case 3:
|
||||
return OUTBOUND_PROGRAMMING_STATUS;
|
||||
case 4:
|
||||
return INBOUND_GENERAL_ACKNOWLEDGE;
|
||||
case 5:
|
||||
return INBOUND_FORWARD_DATA_COMMAND;
|
||||
case 7:
|
||||
return OUTBOUND_FORWARDED_LOGGED_DATA_FRAGMENT;
|
||||
case 8:
|
||||
return OUTBOUND_FORWARDED_REALTIME_DATA;
|
||||
case 9:
|
||||
//Defer to the message factory to detect inbound vs outbound message type
|
||||
return INBOUND_MODULAR_REQUEST;
|
||||
case 10:
|
||||
return OUTBOUND_FIRMWARE_UPDATE;
|
||||
}
|
||||
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
public static MCGPMessageType getOutboundMessageType(int value)
|
||||
{
|
||||
for(MCGPMessageType type: OUTBOUND)
|
||||
{
|
||||
if(type.getValue() == value)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ip.IPacket;
|
||||
import io.github.dsheirer.module.decode.ip.Packet;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Cellocator MCGP Packet
|
||||
*/
|
||||
public abstract class MCGPPacket extends Packet
|
||||
{
|
||||
private MCGPHeader mHeader;
|
||||
|
||||
/**
|
||||
* Constructs a parser for a header contained within a binary message starting at the offset.
|
||||
*
|
||||
* @param header for this message
|
||||
* @param message containing the packet
|
||||
* @param offset to the packet within the message
|
||||
*/
|
||||
public MCGPPacket(MCGPHeader header, BinaryMessage message, int offset)
|
||||
{
|
||||
super(message, offset);
|
||||
mHeader = header;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCGPHeader getHeader()
|
||||
{
|
||||
return mHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit ID (to/from) for this packet
|
||||
*/
|
||||
protected abstract CellocatorRadioIdentifier getRadioId();
|
||||
|
||||
@Override
|
||||
public IPacket getPayload()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
CellocatorRadioIdentifier radioIdentifier = getRadioId();
|
||||
|
||||
if(radioIdentifier != null)
|
||||
{
|
||||
return Collections.singletonList(radioIdentifier);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
/**
|
||||
* Modular message data types
|
||||
*/
|
||||
public enum ModularDataType
|
||||
{
|
||||
FIRMWARE_PLATFORM_MANIFEST(1),
|
||||
CAN_DATA(2),
|
||||
CAN_TRIGGER_DATA(3),
|
||||
TIME_AND_LOCATION_DATA(4),
|
||||
ACCELEROMETER_DATA(5),
|
||||
PSP_ALARM_SYSTEM_DATA(6),
|
||||
USAGE_COUNTER_DATA(7),
|
||||
COMMAND_AUTHENTICATION_TABLE_DATA(8),
|
||||
GSM_NEIGHBOR_LIST(9),
|
||||
MAINTENANCE_SERVER_PLATFORM_MANIFEST(10),
|
||||
UNKNOWN(0);
|
||||
|
||||
private int mValue;
|
||||
|
||||
ModularDataType(int value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public static ModularDataType fromValue(int value)
|
||||
{
|
||||
for(ModularDataType type: ModularDataType.values())
|
||||
{
|
||||
if(type.getValue() == value)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
|
||||
/**
|
||||
* Inbound (from Controller) Modular Request Message
|
||||
*/
|
||||
public class ModularRequestMessage extends MCGPPacket
|
||||
{
|
||||
//This field is byte reversed (ie big endian)
|
||||
private static final int[] TARGET_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] COMMAND_NUMERATOR = new int[]{48, 49, 50, 51, 52, 53, 54, 55};
|
||||
private static final int[] PACKET_CONTROL = new int[]{88, 89, 90, 91, 92, 93, 94, 95};
|
||||
private static final int[] TOTAL_MESSAGE_LENGTH = new int[]{96, 97, 98, 99, 100, 101, 102, 103};
|
||||
private static final int[] SUB_DATA_1_MESSAGE_TYPE = new int[]{104, 105, 106, 107, 108, 109, 110, 111};
|
||||
|
||||
private CellocatorRadioIdentifier mTargetRadioId;
|
||||
private PacketControl mPacketControl;
|
||||
|
||||
/**
|
||||
* Constructs a parser for the message.
|
||||
*
|
||||
* @param header for this message
|
||||
* @param message containing the packet
|
||||
* @param offset to the packet within the message
|
||||
*/
|
||||
public ModularRequestMessage(MCGPHeader header, BinaryMessage message, int offset)
|
||||
{
|
||||
super(header, message, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static access to message total length (bytes) field.
|
||||
* @param message containing modular request message
|
||||
* @param offset to start of modular request message
|
||||
* @return value from the total message length field
|
||||
*/
|
||||
public static int getTotalMessageLength(BinaryMessage message, int offset)
|
||||
{
|
||||
return message.getInt(TOTAL_MESSAGE_LENGTH, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio/Unit identifier, either source or target as specified in the PacketControl field.
|
||||
*/
|
||||
public CellocatorRadioIdentifier getRadioId()
|
||||
{
|
||||
if(mTargetRadioId == null)
|
||||
{
|
||||
int radioId = getMessage().getInt(TARGET_UNIT_ID, getOffset());
|
||||
mTargetRadioId = CellocatorRadioIdentifier.createTo(radioId);
|
||||
}
|
||||
|
||||
return mTargetRadioId;
|
||||
}
|
||||
|
||||
public PacketControl getPacketControl()
|
||||
{
|
||||
if(mPacketControl == null)
|
||||
{
|
||||
mPacketControl = new PacketControl(getMessage().getInt(PACKET_CONTROL, getOffset()));
|
||||
}
|
||||
|
||||
return mPacketControl;
|
||||
}
|
||||
|
||||
public int getCommandNumerator()
|
||||
{
|
||||
return getMessage().getInt(COMMAND_NUMERATOR, getOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Modular data type for the first sub-chunk
|
||||
*/
|
||||
public ModularDataType getModularDataType()
|
||||
{
|
||||
return ModularDataType.fromValue(getMessage().getInt(SUB_DATA_1_MESSAGE_TYPE, getOffset()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CELLOCATOR RADIO:");
|
||||
sb.append(getRadioId());
|
||||
sb.append(" MODULAR REQUEST TYPE:").append(getModularDataType());
|
||||
sb.append(" MESSAGE NUMBER:").append(getCommandNumerator());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
|
||||
/**
|
||||
* Modular Message
|
||||
*/
|
||||
public class ModularResponseMessage extends MCGPPacket
|
||||
{
|
||||
//This field is byte reversed (ie big endian)
|
||||
private static final int[] UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] COMMUNICATION_CONTROL = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
|
||||
46, 47};
|
||||
private static final int[] MESSAGE_NUMERATOR = new int[]{48, 49, 50, 51, 52, 53, 54, 55};
|
||||
private static final int[] PACKET_CONTROL = new int[]{56, 57, 58, 59, 60, 61, 62, 63};
|
||||
private static final int[] TOTAL_MESSAGE_LENGTH = new int[]{64, 65, 66, 67, 68, 69, 70, 71};
|
||||
private static final int[] SUB_DATA_1_MESSAGE_TYPE = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
|
||||
|
||||
private CellocatorRadioIdentifier mRadioId;
|
||||
private CommunicationControl mCommunicationControl;
|
||||
private PacketControl mPacketControl;
|
||||
|
||||
/**
|
||||
* Constructs a parser for the message.
|
||||
*
|
||||
* @param header for this message
|
||||
* @param message containing the packet
|
||||
* @param offset to the packet within the message
|
||||
*/
|
||||
public ModularResponseMessage(MCGPHeader header, BinaryMessage message, int offset)
|
||||
{
|
||||
super(header, message, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static access to message total length (bytes) field.
|
||||
* @param message containing modular response message
|
||||
* @param offset to start of modular response message
|
||||
* @return value from the total message length field
|
||||
*/
|
||||
public static int getTotalMessageLength(BinaryMessage message, int offset)
|
||||
{
|
||||
return message.getInt(TOTAL_MESSAGE_LENGTH, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio/Unit identifier, either source or target as specified in the PacketControl field.
|
||||
*/
|
||||
public CellocatorRadioIdentifier getRadioId()
|
||||
{
|
||||
if(mRadioId == null)
|
||||
{
|
||||
int radioId = getMessage().getInt(UNIT_ID, getOffset());
|
||||
|
||||
if(getPacketControl().getDirection() == PacketControl.Direction.FROM_UNIT)
|
||||
{
|
||||
mRadioId = CellocatorRadioIdentifier.createFrom(radioId);
|
||||
}
|
||||
else
|
||||
{
|
||||
mRadioId = CellocatorRadioIdentifier.createTo(radioId);
|
||||
}
|
||||
}
|
||||
|
||||
return mRadioId;
|
||||
}
|
||||
|
||||
public PacketControl getPacketControl()
|
||||
{
|
||||
if(mPacketControl == null)
|
||||
{
|
||||
mPacketControl = new PacketControl(getMessage().getInt(PACKET_CONTROL, getOffset()));
|
||||
}
|
||||
|
||||
return mPacketControl;
|
||||
}
|
||||
|
||||
public CommunicationControl getCommunicationControl()
|
||||
{
|
||||
if(mCommunicationControl == null)
|
||||
{
|
||||
mCommunicationControl = new CommunicationControl(getMessage().getInt(COMMUNICATION_CONTROL, getOffset()));
|
||||
}
|
||||
|
||||
return mCommunicationControl;
|
||||
}
|
||||
|
||||
public int getMessageNumerator()
|
||||
{
|
||||
return getMessage().getInt(MESSAGE_NUMERATOR, getOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Modular data type for the first sub-chunk
|
||||
*/
|
||||
public ModularDataType getModularDataType()
|
||||
{
|
||||
return ModularDataType.fromValue(getMessage().getInt(SUB_DATA_1_MESSAGE_TYPE, getOffset()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CELLOCATOR RADIO:");
|
||||
if(getPacketControl().getDirection() == PacketControl.Direction.FROM_UNIT)
|
||||
{
|
||||
sb.append(" FROM:");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(" TO:");
|
||||
}
|
||||
sb.append(getRadioId());
|
||||
sb.append(" MODULAR TYPE:").append(getModularDataType());
|
||||
sb.append(" MESSAGE NUMBER:").append(getMessageNumerator());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
/**
|
||||
* Provides fragment information for messages that are too long and have to be fragmented.
|
||||
*/
|
||||
public class PacketControl
|
||||
{
|
||||
public enum Direction {TO_UNIT,FROM_UNIT};
|
||||
private static final int BIT_MASK_DIRECTION = 0x80;
|
||||
private static final int BIT_MASK_OUT_OF_SPACE = 0x40;
|
||||
private int mValue;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param value of the fragment control field byte
|
||||
*/
|
||||
public PacketControl(int value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(" MODULAR PACKET ").append(getDirection());
|
||||
|
||||
if(isOutOfSpace())
|
||||
{
|
||||
sb.append(" (MESSAGE EXCEEDED SPACE)");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the origination of the message fragment
|
||||
* @return origination, unit or application
|
||||
*/
|
||||
public Direction getDirection()
|
||||
{
|
||||
return isSet(BIT_MASK_DIRECTION) ? Direction.TO_UNIT : Direction.FROM_UNIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the message contents exceeded the available space in this message
|
||||
*/
|
||||
public boolean isOutOfSpace()
|
||||
{
|
||||
return isSet(BIT_MASK_OUT_OF_SPACE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the bits that are set in the bit mask are also set in the value field
|
||||
*/
|
||||
private boolean isSet(int bitMask)
|
||||
{
|
||||
return (mValue & bitMask) == bitMask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Controller (Inbound) Programming Command
|
||||
*/
|
||||
public class ProgrammingCommandMessage extends MCGPPacket
|
||||
{
|
||||
//This field is byte reversed (ie big endian)
|
||||
private static final int[] TARGET_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] COMMAND_NUMERATOR = new int[]{32, 33, 34, 35, 36, 37, 38, 39};
|
||||
//This field is (possibly) byte reversed (ie big endian)
|
||||
private static final int[] AUTHENTICATION_CODE = new int[]{64, 65, 66, 67, 68, 69, 70, 71, 56, 57, 58, 59, 60, 61,
|
||||
62, 63, 48, 49, 50, 51, 52, 53, 54, 55, 40, 41, 42, 43, 44, 45, 46, 47};
|
||||
private static final int[] BLOCK_CODE = new int[]{72, 73, 74, 75, 76, 77, 78, 79};
|
||||
private static final int[] BITMASK = new int[]{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95};
|
||||
private static final int DATA_BLOCK_START = 96;
|
||||
private static final int DATA_BLOCK_END = 224;
|
||||
|
||||
private CellocatorRadioIdentifier mTargetRadioId;
|
||||
|
||||
/**
|
||||
* Constructs a parser for a header contained within a binary message starting at the offset.
|
||||
*
|
||||
* @param header for this message
|
||||
* @param message containing the packet
|
||||
* @param offset to the packet within the message
|
||||
*/
|
||||
public ProgrammingCommandMessage(MCGPHeader header, BinaryMessage message, int offset)
|
||||
{
|
||||
super(header, message, offset);
|
||||
}
|
||||
|
||||
public CellocatorRadioIdentifier getRadioId()
|
||||
{
|
||||
if(mTargetRadioId == null)
|
||||
{
|
||||
mTargetRadioId = CellocatorRadioIdentifier.createTo(getMessage().getInt(TARGET_UNIT_ID, getOffset()));
|
||||
}
|
||||
|
||||
return mTargetRadioId;
|
||||
}
|
||||
|
||||
public int getCommandNumerator()
|
||||
{
|
||||
return getMessage().getInt(COMMAND_NUMERATOR, getOffset());
|
||||
}
|
||||
|
||||
public String getAuthenticationCode()
|
||||
{
|
||||
return Integer.toHexString(getMessage().getInt(AUTHENTICATION_CODE, getOffset())).toUpperCase();
|
||||
}
|
||||
|
||||
public int getBlockCode()
|
||||
{
|
||||
return getMessage().getInt(BLOCK_CODE, getOffset());
|
||||
}
|
||||
|
||||
public String getBitMask()
|
||||
{
|
||||
return StringUtils.leftPad(Integer.toHexString(getMessage().getInt(BITMASK, getOffset())).toUpperCase(), 4, "0");
|
||||
}
|
||||
|
||||
public String getDataBlock()
|
||||
{
|
||||
return getMessage().getSubMessage(DATA_BLOCK_START + getOffset(), DATA_BLOCK_END + getOffset()).toHexString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CELLOCATOR RADIO:");
|
||||
sb.append(getRadioId());
|
||||
sb.append(" PROGRAM SEQUENCE NUMBER:").append(getCommandNumerator());
|
||||
sb.append(" AUTH:").append(getAuthenticationCode());
|
||||
sb.append(" BLOCK:").append(getBlockCode());
|
||||
sb.append(" BITMASK:").append(getBitMask());
|
||||
sb.append(" DATA:").append(getDataBlock());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
|
||||
/**
|
||||
* Unit (Outbound) Programming Status/Response
|
||||
*/
|
||||
public class ProgrammingStatusMessage extends MCGPPacket
|
||||
{
|
||||
//This field is byte reversed (ie big endian)
|
||||
private static final int[] SOURCE_UNIT_ID = new int[]{24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
|
||||
private static final int[] COMMUNICATION_CONTROL = new int[]{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
|
||||
46, 47};
|
||||
private static final int[] MESSAGE_NUMERATOR = new int[]{48, 49, 50, 51, 52, 53, 54, 55};
|
||||
private static final int[] SPARE = new int[]{56, 57, 58, 59, 60, 61, 62, 63};
|
||||
private static final int[] BLOCK_CODE = new int[]{64, 65, 66, 67, 68, 69, 70, 71};
|
||||
private static final int DATA_BLOCK_START = 72;
|
||||
private static final int DATA_BLOCK_END = DATA_BLOCK_START + 128;
|
||||
|
||||
private CellocatorRadioIdentifier mSourceRadioId;
|
||||
private CommunicationControl mCommunicationControl;
|
||||
|
||||
/**
|
||||
* Constructs a parser for a header contained within a binary message starting at the offset.
|
||||
*
|
||||
* @param header for this message
|
||||
* @param message containing the packet
|
||||
* @param offset to the packet within the message
|
||||
*/
|
||||
public ProgrammingStatusMessage(MCGPHeader header, BinaryMessage message, int offset)
|
||||
{
|
||||
super(header, message, offset);
|
||||
}
|
||||
|
||||
public CellocatorRadioIdentifier getRadioId()
|
||||
{
|
||||
if(mSourceRadioId == null)
|
||||
{
|
||||
mSourceRadioId = CellocatorRadioIdentifier.createTo(getMessage().getInt(SOURCE_UNIT_ID, getOffset()));
|
||||
}
|
||||
|
||||
return mSourceRadioId;
|
||||
}
|
||||
|
||||
public CommunicationControl getCommunicationControl()
|
||||
{
|
||||
if(mCommunicationControl == null)
|
||||
{
|
||||
mCommunicationControl = new CommunicationControl(getMessage().getInt(COMMUNICATION_CONTROL, getOffset()));
|
||||
}
|
||||
|
||||
return mCommunicationControl;
|
||||
}
|
||||
|
||||
public int getMessageNumerator()
|
||||
{
|
||||
return getMessage().getInt(MESSAGE_NUMERATOR, getOffset());
|
||||
}
|
||||
|
||||
public int getBlockCode()
|
||||
{
|
||||
return getMessage().getInt(BLOCK_CODE, getOffset());
|
||||
}
|
||||
|
||||
public String getDataBlock()
|
||||
{
|
||||
return getMessage().getSubMessage(DATA_BLOCK_START + getOffset(), DATA_BLOCK_END + getOffset()).toHexString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CELLOCATOR RADIO:");
|
||||
sb.append(getRadioId());
|
||||
sb.append(" PROGRAM MESSAGE NUMBER:").append(getMessageNumerator());
|
||||
sb.append(" BLOCK:").append(getBlockCode());
|
||||
sb.append(" DATA:").append(getDataBlock());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ip.cellocator;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.module.decode.ip.IPacket;
|
||||
import io.github.dsheirer.module.decode.ip.UnknownPacket;
|
||||
|
||||
/**
|
||||
* MCGP Packet - Unknown Message Type
|
||||
*/
|
||||
public class UnknownMCGPMessage extends MCGPPacket
|
||||
{
|
||||
private IPacket mPayload;
|
||||
|
||||
/**
|
||||
* Constructs a parser for a header contained within a binary message starting at the offset.
|
||||
*
|
||||
* @param header for this message
|
||||
* @param message containing the packet
|
||||
* @param offset to the packet within the message
|
||||
*/
|
||||
public UnknownMCGPMessage(MCGPHeader header, BinaryMessage message, int offset)
|
||||
{
|
||||
super(header, message, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CELLOCATOR UNKNOWN MESSAGE TYPE [");
|
||||
sb.append(getHeader().getMessageTypeValue()).append("] ").append(getPayload());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPacket getPayload()
|
||||
{
|
||||
if(mPayload == null)
|
||||
{
|
||||
mPayload = new UnknownPacket(getMessage(), getOffset());
|
||||
}
|
||||
|
||||
return mPayload;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CellocatorRadioIdentifier getRadioId()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,13 @@
|
||||
package io.github.dsheirer.module.decode.ip.ipv4;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ip.IPacket;
|
||||
import io.github.dsheirer.module.decode.ip.Packet;
|
||||
import io.github.dsheirer.module.decode.ip.PacketMessageFactory;
|
||||
import io.github.dsheirer.module.decode.ip.UnknownPacket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IPV4 packet
|
||||
@@ -33,6 +36,7 @@ public class IPV4Packet extends Packet
|
||||
{
|
||||
private IPV4Header mHeader;
|
||||
private IPacket mPayload;
|
||||
private List<Identifier> mIdentifiers;
|
||||
|
||||
/**
|
||||
* Constructs an IPV4 packet parser
|
||||
@@ -109,4 +113,23 @@ public class IPV4Packet extends Packet
|
||||
|
||||
return mPayload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
if(mIdentifiers == null)
|
||||
{
|
||||
mIdentifiers = new ArrayList<>();
|
||||
|
||||
if(hasPayload())
|
||||
{
|
||||
mIdentifiers.addAll(getPayload().getIdentifiers());
|
||||
}
|
||||
|
||||
mIdentifiers.add(getHeader().getToAddress());
|
||||
mIdentifiers.add(getHeader().getFromAddress());
|
||||
}
|
||||
|
||||
return mIdentifiers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,15 @@
|
||||
package io.github.dsheirer.module.decode.ip.udp;
|
||||
|
||||
import io.github.dsheirer.bits.BinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ip.IPacket;
|
||||
import io.github.dsheirer.module.decode.ip.Packet;
|
||||
import io.github.dsheirer.module.decode.ip.PacketMessageFactory;
|
||||
import io.github.dsheirer.module.decode.ip.UnknownPacket;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class UDPPacket extends Packet
|
||||
{
|
||||
private UDPHeader mHeader;
|
||||
@@ -73,16 +77,31 @@ public class UDPPacket extends Packet
|
||||
@Override
|
||||
public IPacket getPayload()
|
||||
{
|
||||
if(mPayload == null && getHeader().isValid())
|
||||
if(mPayload == null)
|
||||
{
|
||||
mPayload = PacketMessageFactory.createUDPPayload(getHeader().getDestinationPort(), getMessage(), getOffset() + getHeader().getLength());
|
||||
}
|
||||
else
|
||||
{
|
||||
//Set the offset to the message offset plus 64 bits for UDP header
|
||||
mPayload = new UnknownPacket(getMessage(), getOffset() + 64);
|
||||
if(getHeader().isValid())
|
||||
{
|
||||
mPayload = PacketMessageFactory.createUDPPayload(getHeader().getSourcePort(),
|
||||
getHeader().getDestinationPort(), getMessage(), getOffset() + getHeader().getLength());
|
||||
}
|
||||
else
|
||||
{
|
||||
//Set the offset to the message offset plus 64 bits for UDP header
|
||||
mPayload = new UnknownPacket(getMessage(), getOffset() + 64);
|
||||
}
|
||||
}
|
||||
|
||||
return mPayload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
if(hasPayload())
|
||||
{
|
||||
return getPayload().getIdentifiers();
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.module.decode.ip.IPacket;
|
||||
import io.github.dsheirer.module.decode.ip.ars.ARSPacket;
|
||||
import io.github.dsheirer.module.decode.ip.cellocator.MCGPPacket;
|
||||
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet;
|
||||
import io.github.dsheirer.module.decode.ip.udp.UDPPacket;
|
||||
import io.github.dsheirer.module.decode.p25.P25DecodeEvent;
|
||||
@@ -935,6 +936,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
ARSPacket arsPacket = (ARSPacket)udpPayload;
|
||||
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
for(Identifier identifier: packet.getIdentifiers())
|
||||
{
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
@@ -945,9 +950,32 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
|
||||
broadcast(packetEvent);
|
||||
}
|
||||
else if(udpPayload instanceof MCGPPacket)
|
||||
{
|
||||
MCGPPacket mcgpPacket = (MCGPPacket)udpPayload;
|
||||
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
for(Identifier identifier: packet.getIdentifiers())
|
||||
{
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent cellocatorEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription("Cellocator")
|
||||
.identifiers(ic)
|
||||
.details(mcgpPacket.toString() + " " + ipv4.toString())
|
||||
.build();
|
||||
|
||||
broadcast(cellocatorEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
for(Identifier identifier: packet.getIdentifiers())
|
||||
{
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
|
||||
@@ -25,7 +25,6 @@ import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.alias.id.record.Record;
|
||||
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
||||
import io.github.dsheirer.bits.BitSetFullException;
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
@@ -34,7 +33,6 @@ import io.github.dsheirer.dsp.psk.pll.IPhaseLockedLoop;
|
||||
import io.github.dsheirer.dsp.symbol.Dibit;
|
||||
import io.github.dsheirer.dsp.symbol.ISyncDetectListener;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.IMessageProvider;
|
||||
import io.github.dsheirer.message.Message;
|
||||
import io.github.dsheirer.message.MessageProviderModule;
|
||||
import io.github.dsheirer.message.StuffBitsMessage;
|
||||
@@ -42,23 +40,17 @@ import io.github.dsheirer.message.SyncLossMessage;
|
||||
import io.github.dsheirer.module.ProcessingChain;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.p25.audio.P25P1AudioModule;
|
||||
import io.github.dsheirer.module.decode.p25.audio.P25P2AudioModule;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.P25Message;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.P25MessageFactory;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.PDUMessageFactory;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.PDUSequence;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessageFactory;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderState;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.P25P2MessageFramer;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.P25P2MessageProcessor;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.enumeration.ScrambleParameters;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import io.github.dsheirer.record.AudioRecordingManager;
|
||||
import io.github.dsheirer.record.binary.BinaryReader;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import org.slf4j.Logger;
|
||||
@@ -201,6 +193,17 @@ public class P25P1MessageFramer implements Listener<Dibit>, IP25P1DataUnitDetect
|
||||
{
|
||||
mBinaryMessage.add(dibit.getBit1());
|
||||
mBinaryMessage.add(dibit.getBit2());
|
||||
|
||||
if(mBinaryMessage.isFull())
|
||||
{
|
||||
//TDU's have a trailing status symbol that has to be removed -- set flag to true to suppress it.
|
||||
if(mDataUnitID.hasTrailingStatusDibit())
|
||||
{
|
||||
mTrailingDibitsToSuppress = 1;
|
||||
}
|
||||
|
||||
dispatchMessage();
|
||||
}
|
||||
}
|
||||
catch(BitSetFullException bsfe)
|
||||
{
|
||||
@@ -209,17 +212,6 @@ public class P25P1MessageFramer implements Listener<Dibit>, IP25P1DataUnitDetect
|
||||
//Reset so that we can start over again
|
||||
reset(0);
|
||||
}
|
||||
|
||||
if(mBinaryMessage.isFull())
|
||||
{
|
||||
//TDU's have a trailing status symbol that has to be removed -- set flag to true to suppress it.
|
||||
if(mDataUnitID.hasTrailingStatusDibit())
|
||||
{
|
||||
mTrailingDibitsToSuppress = 1;
|
||||
}
|
||||
|
||||
dispatchMessage();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -458,7 +450,7 @@ public class P25P1MessageFramer implements Listener<Dibit>, IP25P1DataUnitDetect
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Path directory = Paths.get("/home/denny/temp/Harris P25-2 Logs/bits");
|
||||
Path directory = Paths.get("/home/denny/Documents/TMR/APCO25/GPS/cnyicc_cellocator");
|
||||
// Path directory = Paths.get("/media/denny/500G1EXT4/PBITRecordings");
|
||||
|
||||
UserPreferences userPreferences = new UserPreferences();
|
||||
@@ -479,11 +471,13 @@ public class P25P1MessageFramer implements Listener<Dibit>, IP25P1DataUnitDetect
|
||||
MessageProviderModule messageProviderModule = new MessageProviderModule();
|
||||
processingChain.addModule(messageProviderModule);
|
||||
|
||||
mLog.info("Processing Directory: " + directory.toString());
|
||||
|
||||
try(OutputStream logOutput = Files.newOutputStream(directory.resolve("log.txt")))
|
||||
{
|
||||
try
|
||||
{
|
||||
DirectoryStream<Path> stream = Files.newDirectoryStream(directory, "*PHASE1_Hills*TRAFFIC.bits");
|
||||
DirectoryStream<Path> stream = Files.newDirectoryStream(directory, "*.bits");
|
||||
|
||||
stream.forEach(new Consumer<Path>()
|
||||
{
|
||||
@@ -520,7 +514,11 @@ public class P25P1MessageFramer implements Listener<Dibit>, IP25P1DataUnitDetect
|
||||
{
|
||||
mLog.error("Error", ioe);
|
||||
}
|
||||
mLog.debug(message.toString());
|
||||
|
||||
if(message instanceof PacketMessage)
|
||||
{
|
||||
mLog.debug(message.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -264,11 +264,9 @@ public class PacketMessage extends P25Message
|
||||
mIdentifiers = new ArrayList<>();
|
||||
mIdentifiers.add(getPDUSequence().getHeader().getLLID());
|
||||
|
||||
if(getPacket() instanceof IPV4Packet)
|
||||
if(getPacket() != null)
|
||||
{
|
||||
IPV4Packet ipv4 = (IPV4Packet)getPacket();
|
||||
mIdentifiers.add(ipv4.getHeader().getFromAddress());
|
||||
mIdentifiers.add(ipv4.getHeader().getToAddress());
|
||||
mIdentifiers.addAll(getPacket().getIdentifiers());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ public enum Protocol
|
||||
{
|
||||
APCO25("APCO-25", "APCO25PHASE1", 9600),
|
||||
APCO25_PHASE2("APCO-25 P2", "APCO25PHASE2", 12000),
|
||||
CELLOCATOR("CELLOCATOR", "CELLOCATOR", 0),
|
||||
FLEETSYNC("Fleetsync", "FLEETSYNC", 1200),
|
||||
IPV4("IPV4", "IPV4", 0),
|
||||
LOJACK("LoJack", "LOJACK", 1200),
|
||||
|
||||
@@ -157,10 +157,10 @@ public class AudioRecordingManager implements Listener<AudioSegment>
|
||||
}
|
||||
|
||||
audioSegment.decrementConsumerCount();
|
||||
|
||||
//Grab the next one to record
|
||||
audioSegment = mCompletedAudioSegmentQueue.poll();
|
||||
}
|
||||
|
||||
//Grab the next one to record
|
||||
audioSegment = mCompletedAudioSegmentQueue.poll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import io.github.dsheirer.sample.adapter.RealChannelShortAdapter;
|
||||
import io.github.dsheirer.sample.adapter.RealShortAdapter;
|
||||
import io.github.dsheirer.source.config.SourceConfigMixer;
|
||||
import io.github.dsheirer.source.config.SourceConfiguration;
|
||||
import io.github.dsheirer.source.tuner.MixerTunerDataLine;
|
||||
import io.github.dsheirer.source.tuner.MixerTunerType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -35,7 +34,6 @@ import javax.sound.sampled.Port;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
import javax.sound.sampled.TargetDataLine;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
@@ -46,7 +44,10 @@ public class MixerManager
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MixerManager.class);
|
||||
|
||||
public MixerManager() {}
|
||||
public MixerManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static RealMixerSource getSource(SourceConfiguration config)
|
||||
{
|
||||
@@ -193,33 +194,69 @@ public class MixerManager
|
||||
return outputMixers;
|
||||
}
|
||||
|
||||
public static Collection<MixerTunerDataLine> getMixerTunerDataLines()
|
||||
/**
|
||||
* Obtains a target data line for the specified mixer tuner type
|
||||
* @param mixerTunerType to find
|
||||
* @return target data line or null.
|
||||
*/
|
||||
public static TargetDataLine getTunerTargetDataLine(MixerTunerType mixerTunerType)
|
||||
{
|
||||
List<MixerTunerDataLine> tuners = new ArrayList<>();
|
||||
|
||||
for(Mixer.Info mixerInfo : AudioSystem.getMixerInfo())
|
||||
{
|
||||
//Sort between the mixers and the tuner mixers, and load each
|
||||
MixerTunerType mixerTunerType = MixerTunerType.getMixerTunerType(mixerInfo);
|
||||
MixerTunerType type = MixerTunerType.getMixerTunerType(mixerInfo);
|
||||
|
||||
if(mixerTunerType != MixerTunerType.UNKNOWN)
|
||||
if(type != null && type == mixerTunerType)
|
||||
{
|
||||
TargetDataLine tdl = getTargetDataLine(mixerInfo, mixerTunerType.getAudioFormat());
|
||||
Mixer mixer = AudioSystem.getMixer(mixerInfo);
|
||||
|
||||
if(tdl != null)
|
||||
if(mixer != null)
|
||||
{
|
||||
switch(mixerTunerType)
|
||||
for(Line.Info info : mixer.getTargetLineInfo())
|
||||
{
|
||||
case FUNCUBE_DONGLE_PRO:
|
||||
case FUNCUBE_DONGLE_PRO_PLUS:
|
||||
tuners.add(new MixerTunerDataLine(tdl, mixerTunerType));
|
||||
break;
|
||||
if(info instanceof DataLine.Info)
|
||||
{
|
||||
try
|
||||
{
|
||||
Line line = mixer.getLine(info);
|
||||
|
||||
if(line instanceof TargetDataLine)
|
||||
{
|
||||
return (TargetDataLine)line;
|
||||
}
|
||||
}
|
||||
catch(LineUnavailableException lue)
|
||||
{
|
||||
mLog.error("Line Unavailable. Unable to get TargetDataLine for Mixer Tuner: " + mixerTunerType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tuners;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the target data line supported format that most closely matches the specified audio format without
|
||||
* matching sample rates.
|
||||
* @param info for a data line
|
||||
* @param audioFormat to find a match
|
||||
* @return matching audio format or null.
|
||||
*/
|
||||
public static AudioFormat getMatchingFormat(DataLine.Info info, AudioFormat audioFormat)
|
||||
{
|
||||
for(AudioFormat audioFormatToTest: info.getFormats())
|
||||
{
|
||||
if(audioFormat.getSampleSizeInBits() == audioFormatToTest.getSampleSizeInBits() &&
|
||||
audioFormat.getChannels() == audioFormatToTest.getChannels() &&
|
||||
!(audioFormat.isBigEndian() ^ audioFormatToTest.isBigEndian()))
|
||||
{
|
||||
return audioFormatToTest;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static TargetDataLine getTargetDataLine(Mixer.Info mixerInfo, AudioFormat format)
|
||||
@@ -232,6 +269,20 @@ public class MixerManager
|
||||
{
|
||||
try
|
||||
{
|
||||
for(Line line: mixer.getTargetLines())
|
||||
{
|
||||
mLog.debug("Line: " + line.getLineInfo().toString());
|
||||
}
|
||||
|
||||
for(Line line: mixer.getSourceLines())
|
||||
{
|
||||
mLog.debug("Line: " + line.getLineInfo().toString());
|
||||
}
|
||||
|
||||
Mixer.Info info = mixer.getMixerInfo();
|
||||
|
||||
mLog.debug(info.toString());
|
||||
|
||||
DataLine.Info datalineInfo = new DataLine.Info(TargetDataLine.class, format);
|
||||
|
||||
retVal = (TargetDataLine)mixer.getLine(datalineInfo);
|
||||
@@ -318,6 +369,11 @@ public class MixerManager
|
||||
.append("\n");
|
||||
|
||||
Mixer mixer = AudioSystem.getMixer(mixerInfo);
|
||||
Line.Info lineInfo1 = mixer.getLineInfo();
|
||||
|
||||
String a = lineInfo1.toString();
|
||||
|
||||
Line.Info[] infos = mixer.getTargetLineInfo();
|
||||
|
||||
Line.Info[] sourceLines = mixer.getSourceLineInfo();
|
||||
|
||||
@@ -337,6 +393,41 @@ public class MixerManager
|
||||
.append("\n class:").append(lineInfo.getClass())
|
||||
.append("\n lineclass:").append(lineInfo.getLineClass())
|
||||
.append("\n");
|
||||
|
||||
if(lineInfo instanceof DataLine.Info)
|
||||
{
|
||||
DataLine.Info dli = (DataLine.Info)lineInfo;
|
||||
|
||||
for(AudioFormat format: dli.getFormats())
|
||||
{
|
||||
sb.append(" FORMAT:").append(format.toString()).append("\n");
|
||||
|
||||
if(mixerInfo.getName().startsWith("V"))
|
||||
{
|
||||
sb.append("Iterating formats for " + mixerInfo.getName() + " " + mixerInfo.getDescription()).append("\n");
|
||||
try
|
||||
{
|
||||
Line line = mixer.getLine(lineInfo);
|
||||
sb.append(line.getLineInfo().toString()).append("\n");
|
||||
|
||||
if(line instanceof TargetDataLine)
|
||||
{
|
||||
TargetDataLine tdl = (TargetDataLine)line;
|
||||
tdl.open();
|
||||
tdl.start();
|
||||
byte[] bytes = new byte[1024];
|
||||
int read = tdl.read(bytes, 0, bytes.length);
|
||||
sb.append("READ:" + read).append("\n");
|
||||
tdl.close();
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Line.Info portInfo = new Line.Info(Port.class);
|
||||
@@ -389,9 +480,35 @@ public class MixerManager
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
for(MixerChannelConfiguration config : getOutputMixers())
|
||||
MixerTunerType mixerTunerType = MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS;
|
||||
TargetDataLine targetDataLine = getTunerTargetDataLine(mixerTunerType);
|
||||
|
||||
if(targetDataLine != null)
|
||||
{
|
||||
mLog.debug(config.toString());
|
||||
try
|
||||
{
|
||||
targetDataLine.open(mixerTunerType.getAudioFormat());
|
||||
mLog.info(targetDataLine.getFormat().toString());
|
||||
targetDataLine.start();
|
||||
byte[] bytes = new byte[1024];
|
||||
int read = targetDataLine.read(bytes, 0, bytes.length);
|
||||
|
||||
while(read > 0)
|
||||
{
|
||||
mLog.debug("Read:" + read);
|
||||
read = 0;
|
||||
// read = targetDataLine.read(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
targetDataLine.close();
|
||||
|
||||
mLog.debug("Done!");
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,10 +66,9 @@ public class MixerReader<T extends ReusableFloatBuffer> implements Runnable
|
||||
mBufferSize = (int)(mAudioFormat.getSampleRate() * 0.1) * mAudioFormat.getFrameSize();
|
||||
}
|
||||
|
||||
public MixerReader(AudioFormat audioFormat, TargetDataLine targetDataLine,
|
||||
AbstractSampleAdapter<T> abstractSampleAdapter)
|
||||
public MixerReader(AudioFormat audioFormat, TargetDataLine targetDataLine, AbstractSampleAdapter<T> abstractSampleAdapter)
|
||||
{
|
||||
this(audioFormat, targetDataLine, abstractSampleAdapter, null);
|
||||
this(audioFormat, targetDataLine, abstractSampleAdapter, new HeartbeatManager());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,8 +103,12 @@ public class MixerReader<T extends ReusableFloatBuffer> implements Runnable
|
||||
|
||||
mBuffer = new byte[mBufferSize];
|
||||
|
||||
mTargetDataLine.open(mAudioFormat);
|
||||
mTargetDataLine.open(getAudioFormat());
|
||||
mLog.info("TDL Open:" + mTargetDataLine.isOpen() + " Active:" + mTargetDataLine.isActive() + " Running:" + mTargetDataLine.isRunning());
|
||||
mLog.info("Format:" + mTargetDataLine.getFormat().toString());
|
||||
mTargetDataLine.start();
|
||||
mLog.info("TDL Open:" + mTargetDataLine.isOpen() + " Active:" + mTargetDataLine.isActive() + " Running:" + mTargetDataLine.isRunning());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +118,15 @@ public class MixerReader<T extends ReusableFloatBuffer> implements Runnable
|
||||
{
|
||||
if(mTargetDataLine != null && mTargetDataLine.isOpen())
|
||||
{
|
||||
mTargetDataLine.close();
|
||||
if(mTargetDataLine.isRunning())
|
||||
{
|
||||
mTargetDataLine.stop();
|
||||
}
|
||||
|
||||
if(mTargetDataLine.isOpen())
|
||||
{
|
||||
mTargetDataLine.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,9 +226,9 @@ public class MixerReader<T extends ReusableFloatBuffer> implements Runnable
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("MixerSource - error while reading from the mixer target data line", e);
|
||||
mLog.error("MixerSource - error while reading from the mixer target data line", t);
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.source.tuner;
|
||||
|
||||
import javax.sound.sampled.TargetDataLine;
|
||||
|
||||
public class MixerTunerDataLine
|
||||
{
|
||||
private TargetDataLine mTargetDataLine;
|
||||
protected MixerTunerType mMixerTunerType;
|
||||
|
||||
public MixerTunerDataLine( TargetDataLine line, MixerTunerType type )
|
||||
{
|
||||
mTargetDataLine = line;
|
||||
mMixerTunerType = type;
|
||||
}
|
||||
|
||||
public TargetDataLine getTargetDataLine()
|
||||
{
|
||||
return mTargetDataLine;
|
||||
}
|
||||
|
||||
public MixerTunerType getMixerTunerType()
|
||||
{
|
||||
return mMixerTunerType;
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,8 @@ import org.usb4java.DeviceDescriptor;
|
||||
import org.usb4java.DeviceList;
|
||||
import org.usb4java.LibUsb;
|
||||
|
||||
import javax.sound.sampled.TargetDataLine;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -285,13 +285,11 @@ public class TunerManager
|
||||
}
|
||||
}
|
||||
|
||||
private TunerInitStatus initTuner(Device device,
|
||||
DeviceDescriptor descriptor)
|
||||
private TunerInitStatus initTuner(Device device, DeviceDescriptor descriptor)
|
||||
{
|
||||
if(device != null && descriptor != null)
|
||||
{
|
||||
TunerClass tunerClass = TunerClass.valueOf(descriptor.idVendor(),
|
||||
descriptor.idProduct());
|
||||
TunerClass tunerClass = TunerClass.valueOf(descriptor.idVendor(), descriptor.idProduct());
|
||||
|
||||
switch(tunerClass)
|
||||
{
|
||||
@@ -358,8 +356,7 @@ public class TunerManager
|
||||
}
|
||||
}
|
||||
|
||||
private TunerInitStatus initAirspyTuner(Device device,
|
||||
DeviceDescriptor descriptor)
|
||||
private TunerInitStatus initAirspyTuner(Device device, DeviceDescriptor descriptor)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -381,35 +378,30 @@ public class TunerManager
|
||||
}
|
||||
|
||||
|
||||
private TunerInitStatus initEttusB100Tuner(Device device,
|
||||
DeviceDescriptor descriptor)
|
||||
private TunerInitStatus initEttusB100Tuner(Device device, DeviceDescriptor descriptor)
|
||||
{
|
||||
return new TunerInitStatus(null, "Ettus B100 tuner not currently "
|
||||
+ "supported");
|
||||
return new TunerInitStatus(null, "Ettus B100 tuner not currently supported");
|
||||
}
|
||||
|
||||
private TunerInitStatus initFuncubeProTuner(Device device, DeviceDescriptor descriptor)
|
||||
{
|
||||
String reason = "NOT LOADED";
|
||||
|
||||
MixerTunerDataLine dataline = getMixerTunerDataLine(TunerClass.FUNCUBE_DONGLE_PRO.getTunerType());
|
||||
TargetDataLine tdl = MixerManager.getTunerTargetDataLine(MixerTunerType.FUNCUBE_DONGLE_PRO);
|
||||
|
||||
if(dataline != null)
|
||||
if(tdl != null)
|
||||
{
|
||||
FCD1TunerController controller = new FCD1TunerController(dataline, device, descriptor);
|
||||
FCD1TunerController controller = new FCD1TunerController(tdl, device, descriptor);
|
||||
|
||||
try
|
||||
{
|
||||
controller.init();
|
||||
|
||||
FCDTuner tuner = new FCDTuner(controller, mUserPreferences);
|
||||
|
||||
return new TunerInitStatus(tuner, "LOADED");
|
||||
}
|
||||
catch(SourceException e)
|
||||
{
|
||||
mLog.error("couldn't load funcube dongle pro tuner", e);
|
||||
|
||||
reason = "error during initialization - " + e.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
@@ -418,15 +410,14 @@ public class TunerManager
|
||||
reason = "couldn't find matching mixer dataline";
|
||||
}
|
||||
|
||||
return new TunerInitStatus(null, "Funcube Dongle Pro tuner not "
|
||||
+ "loaded - " + reason);
|
||||
return new TunerInitStatus(null, "Funcube Dongle Pro tuner not loaded - " + reason);
|
||||
}
|
||||
|
||||
private TunerInitStatus initFuncubeProPlusTuner(Device device, DeviceDescriptor descriptor)
|
||||
{
|
||||
String reason = "NOT LOADED";
|
||||
|
||||
MixerTunerDataLine dataline = getMixerTunerDataLine(TunerClass.FUNCUBE_DONGLE_PRO_PLUS.getTunerType());
|
||||
TargetDataLine dataline = MixerManager.getTunerTargetDataLine(MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS);
|
||||
|
||||
if(dataline != null)
|
||||
{
|
||||
@@ -435,17 +426,13 @@ public class TunerManager
|
||||
try
|
||||
{
|
||||
controller.init();
|
||||
|
||||
FCDTuner tuner = new FCDTuner(controller, mUserPreferences);
|
||||
|
||||
return new TunerInitStatus(tuner, "LOADED");
|
||||
}
|
||||
catch(SourceException e)
|
||||
{
|
||||
mLog.error("couldn't load funcube dongle pro plus tuner", e);
|
||||
|
||||
reason = "error during initialization - " +
|
||||
e.getLocalizedMessage();
|
||||
reason = "error during initialization - " + e.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -453,8 +440,7 @@ public class TunerManager
|
||||
reason = "couldn't find matching mixer dataline";
|
||||
}
|
||||
|
||||
return new TunerInitStatus(null, "Funcube Dongle Pro tuner not "
|
||||
+ "loaded - " + reason);
|
||||
return new TunerInitStatus(null, "Funcube Dongle Pro tuner not loaded - " + reason);
|
||||
}
|
||||
|
||||
private TunerInitStatus initHackRFTuner(Device device, DeviceDescriptor descriptor)
|
||||
@@ -473,14 +459,11 @@ public class TunerManager
|
||||
{
|
||||
mLog.error("couldn't construct HackRF controller/tuner", se);
|
||||
|
||||
return new TunerInitStatus(null,
|
||||
"error constructing HackRF tuner controller");
|
||||
return new TunerInitStatus(null, "error constructing HackRF tuner controller");
|
||||
}
|
||||
}
|
||||
|
||||
private TunerInitStatus initRTL2832Tuner(TunerClass tunerClass,
|
||||
Device device,
|
||||
DeviceDescriptor deviceDescriptor)
|
||||
private TunerInitStatus initRTL2832Tuner(TunerClass tunerClass, Device device, DeviceDescriptor deviceDescriptor)
|
||||
{
|
||||
String reason = "NOT LOADED";
|
||||
|
||||
@@ -540,8 +523,7 @@ public class TunerManager
|
||||
case RAFAELMICRO_R828D:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
reason = "SDRTRunk doesn't currently support RTL2832 "
|
||||
+ "Dongle with [" + tunerType.toString() +
|
||||
reason = "SDRTRunk doesn't currently support RTL2832 Dongle with [" + tunerType.toString() +
|
||||
"] tuner for tuner class[" + tunerClass.toString() + "]";
|
||||
break;
|
||||
}
|
||||
@@ -549,32 +531,6 @@ public class TunerManager
|
||||
return new TunerInitStatus(null, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first tuner mixer dataline that corresponds to the tuner class.
|
||||
*
|
||||
* Note: this method is not currently able to align multiple tuner mixer
|
||||
* data lines of the same tuner type. If you have multiple Funcube Dongle
|
||||
* tuners of the same TYPE, there is no guarantee that you will get the
|
||||
* correct mixer.
|
||||
*
|
||||
* @param tunerClass
|
||||
* @return
|
||||
*/
|
||||
private MixerTunerDataLine getMixerTunerDataLine(TunerType tunerClass)
|
||||
{
|
||||
Collection<MixerTunerDataLine> datalines = MixerManager.getMixerTunerDataLines();
|
||||
|
||||
for(MixerTunerDataLine mixerTDL : datalines)
|
||||
{
|
||||
if(mixerTDL.getMixerTunerType().getTunerClass() == tunerClass)
|
||||
{
|
||||
return mixerTDL;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public class TunerInitStatus
|
||||
{
|
||||
private Tuner mTuner;
|
||||
|
||||
@@ -23,7 +23,7 @@ import io.github.dsheirer.sample.adapter.ComplexShortAdapter;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.mixer.ComplexMixer;
|
||||
import io.github.dsheirer.source.tuner.MixerTunerDataLine;
|
||||
import io.github.dsheirer.source.tuner.MixerTunerType;
|
||||
import io.github.dsheirer.source.tuner.TunerClass;
|
||||
import io.github.dsheirer.source.tuner.TunerController;
|
||||
import io.github.dsheirer.source.tuner.TunerType;
|
||||
@@ -36,7 +36,7 @@ import org.usb4java.DeviceHandle;
|
||||
import org.usb4java.LibUsb;
|
||||
import org.usb4java.LibUsbException;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.TargetDataLine;
|
||||
import javax.usb.UsbClaimException;
|
||||
import javax.usb.UsbException;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -71,8 +71,8 @@ public abstract class FCDTunerController extends TunerController
|
||||
* @param minTunableFrequency
|
||||
* @param maxTunableFrequency
|
||||
*/
|
||||
public FCDTunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor, String tunerName,
|
||||
int minTunableFrequency, int maxTunableFrequency, AudioFormat audioFormat)
|
||||
public FCDTunerController(MixerTunerType tunerType, TargetDataLine mixerTDL, Device device,
|
||||
DeviceDescriptor descriptor, int minTunableFrequency, int maxTunableFrequency)
|
||||
{
|
||||
super(minTunableFrequency, maxTunableFrequency, DC_SPIKE_AVOID_BUFFER, USABLE_BANDWIDTH_PERCENT);
|
||||
mDevice = device;
|
||||
@@ -80,17 +80,17 @@ public abstract class FCDTunerController extends TunerController
|
||||
|
||||
try
|
||||
{
|
||||
mFrequencyController.setSampleRate((int)audioFormat.getSampleRate());
|
||||
mFrequencyController.setSampleRate((int)tunerType.getAudioFormat().getSampleRate());
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error setting sample rate to [" + audioFormat.getSampleRate() + "]", se);
|
||||
mLog.error("Error setting sample rate to [" + tunerType.getAudioFormat().getSampleRate() + "]", se);
|
||||
}
|
||||
|
||||
mComplexMixer = new ComplexMixer( mixerTDL.getTargetDataLine(), audioFormat, tunerName,
|
||||
new ComplexShortAdapter(mixerTDL.getMixerTunerType().getDisplayString()));
|
||||
mComplexMixer = new ComplexMixer( mixerTDL, tunerType.getAudioFormat(), tunerType.getDisplayString(),
|
||||
new ComplexShortAdapter(tunerType.getDisplayString()));
|
||||
|
||||
mComplexMixer.setBufferSize(audioFormat.getFrameSize() * getBufferSampleCount());
|
||||
mComplexMixer.setBufferSize(tunerType.getAudioFormat().getFrameSize() * getBufferSampleCount());
|
||||
|
||||
mComplexMixer.setBufferListener(mReusableBufferBroadcaster);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package io.github.dsheirer.source.tuner.fcd.proV1;
|
||||
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.MixerTunerDataLine;
|
||||
import io.github.dsheirer.source.tuner.MixerTunerType;
|
||||
import io.github.dsheirer.source.tuner.TunerClass;
|
||||
import io.github.dsheirer.source.tuner.TunerType;
|
||||
@@ -30,6 +29,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.usb4java.Device;
|
||||
import org.usb4java.DeviceDescriptor;
|
||||
|
||||
import javax.sound.sampled.TargetDataLine;
|
||||
import javax.usb.UsbClaimException;
|
||||
import javax.usb.UsbException;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -53,10 +53,10 @@ public class FCD1TunerController extends FCDTunerController
|
||||
private FCD1TunerConfiguration mTunerConfiguration;
|
||||
private FCD1TunerEditor mEditor;
|
||||
|
||||
public FCD1TunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor)
|
||||
public FCD1TunerController(TargetDataLine mixerTDL, Device device, DeviceDescriptor descriptor)
|
||||
{
|
||||
super(mixerTDL, device, descriptor, MixerTunerType.FUNCUBE_DONGLE_PRO.getDisplayString(), MINIMUM_TUNABLE_FREQUENCY,
|
||||
MAXIMUM_TUNABLE_FREQUENCY, MixerTunerType.FUNCUBE_DONGLE_PRO.getAudioFormat());
|
||||
super(MixerTunerType.FUNCUBE_DONGLE_PRO, mixerTDL, device, descriptor,
|
||||
MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY);
|
||||
}
|
||||
|
||||
public void init() throws SourceException
|
||||
@@ -78,10 +78,7 @@ public class FCD1TunerController extends FCDTunerController
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
|
||||
throw new SourceException("FCDTunerController error " +
|
||||
"during construction", e);
|
||||
throw new SourceException("FCDTunerController error during construction", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package io.github.dsheirer.source.tuner.fcd.proplusV2;
|
||||
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.MixerTunerDataLine;
|
||||
import io.github.dsheirer.source.tuner.MixerTunerType;
|
||||
import io.github.dsheirer.source.tuner.TunerClass;
|
||||
import io.github.dsheirer.source.tuner.TunerType;
|
||||
@@ -31,6 +30,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.usb4java.Device;
|
||||
import org.usb4java.DeviceDescriptor;
|
||||
|
||||
import javax.sound.sampled.TargetDataLine;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
@@ -42,10 +42,10 @@ public class FCD2TunerController extends FCDTunerController
|
||||
public static final int MAXIMUM_TUNABLE_FREQUENCY = 2050000000;
|
||||
public static final int SAMPLE_RATE = 192000;
|
||||
|
||||
public FCD2TunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor)
|
||||
public FCD2TunerController(TargetDataLine mixerTDL, Device device, DeviceDescriptor descriptor)
|
||||
{
|
||||
super(mixerTDL, device, descriptor, MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS.getDisplayString(),
|
||||
MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY, MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS.getAudioFormat());
|
||||
super(MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS, mixerTDL, device, descriptor,
|
||||
MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY);
|
||||
}
|
||||
|
||||
public void init() throws SourceException
|
||||
|
||||
Reference in New Issue
Block a user