From 05c0b51fde72ecc6008486e87b33f9bfc88e04b7 Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Sun, 25 Oct 2020 06:09:57 -0400 Subject: [PATCH] #979 Updates DMR Tier III channel number issues and provides user option to ignore CRC checksums for DMR decoder with RAS-enabled systems. Adds support for Tier III traffic channel system parameters SLCO message. (#980) Co-authored-by: Denny --- .../channel/DMRConfigurationEditor.java | 40 +++++-- .../module/decode/dmr/DMRDecoderState.java | 48 +++++--- .../dmr/DMRNetworkConfigurationMonitor.java | 2 +- .../module/decode/dmr/DecodeConfigDMR.java | 19 +++ .../decode/dmr/channel/DMRTier3Channel.java | 40 +++++++ .../dmr/message/data/csbk/standard/Clear.java | 3 +- .../message/data/csbk/standard/MoveTSCC.java | 4 +- .../announcement/AdjacentSiteInformation.java | 3 +- .../announcement/AnnounceWithdrawTSCC.java | 6 +- .../standard/announcement/VoteNowAdvice.java | 3 +- .../csbk/standard/grant/ChannelGrant.java | 3 +- .../dmr/message/data/lc/LCMessageFactory.java | 5 +- .../TrafficChannelSystemParameters.java | 112 ++++++++++++++++++ 13 files changed, 251 insertions(+), 37 deletions(-) create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRTier3Channel.java create mode 100644 src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/TrafficChannelSystemParameters.java diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java index 128d6a45..e64a3057 100644 --- a/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java @@ -84,6 +84,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor private EventLogConfigurationEditor mEventLogConfigurationEditor; private RecordConfigurationEditor mRecordConfigurationEditor; private ToggleSwitch mIgnoreDataCallsButton; + private ToggleSwitch mIgnoreCRCChecksumsButton; private Spinner mTrafficChannelPoolSizeSpinner; private TableView mTimeslotFrequencyTable; private IntegerTextField mLogicalSlotNumberField; @@ -149,17 +150,25 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor GridPane.setConstraints(getIgnoreDataCallsButton(), 2, row); gridPane.getChildren().add(getIgnoreDataCallsButton()); - Label directionLabel = new Label("Ignore Data Calls"); - GridPane.setHalignment(directionLabel, HPos.LEFT); - GridPane.setConstraints(directionLabel, 3, row); - gridPane.getChildren().add(directionLabel); + Label ignoreDataLabel = new Label("Ignore Data Calls"); + GridPane.setHalignment(ignoreDataLabel, HPos.LEFT); + GridPane.setConstraints(ignoreDataLabel, 3, row); + gridPane.getChildren().add(ignoreDataLabel); + + GridPane.setConstraints(getIgnoreCRCChecksumsButton(), 4, row); + gridPane.getChildren().add(getIgnoreCRCChecksumsButton()); + + Label ignoreCRCLabel = new Label("Ignore CRC Checksums (RAS)"); + GridPane.setHalignment(ignoreCRCLabel, HPos.LEFT); + GridPane.setConstraints(ignoreCRCLabel, 5, row); + gridPane.getChildren().add(ignoreCRCLabel); Label timeslotTableLabel = new Label("Logical Slot Number (LSN) to Frequency Map. Required for: Connect Plus and Tier-III systems that don't use absolute frequencies"); GridPane.setHalignment(timeslotTableLabel, HPos.LEFT); - GridPane.setConstraints(timeslotTableLabel, 0, ++row, 4, 1); + GridPane.setConstraints(timeslotTableLabel, 0, ++row, 6, 1); gridPane.getChildren().add(timeslotTableLabel); - GridPane.setConstraints(getTimeslotTable(), 0, ++row, 4, 3); + GridPane.setConstraints(getTimeslotTable(), 0, ++row, 6, 3); gridPane.getChildren().add(getTimeslotTable()); VBox buttonsBox = new VBox(); @@ -167,7 +176,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor buttonsBox.setSpacing(10); buttonsBox.getChildren().addAll(getAddTimeslotFrequencyButton(), getDeleteTimeslotFrequencyButton()); - GridPane.setConstraints(buttonsBox, 4, row, 1, 3); + GridPane.setConstraints(buttonsBox, 6, row, 1, 3); gridPane.getChildren().addAll(buttonsBox); row += 3; @@ -489,6 +498,19 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor return mIgnoreDataCallsButton; } + private ToggleSwitch getIgnoreCRCChecksumsButton() + { + if(mIgnoreCRCChecksumsButton == null) + { + mIgnoreCRCChecksumsButton = new ToggleSwitch(); + mIgnoreCRCChecksumsButton.setDisable(true); + mIgnoreCRCChecksumsButton.selectedProperty() + .addListener((observable, oldValue, newValue) -> modifiedProperty().set(true)); + } + + return mIgnoreCRCChecksumsButton; + } + private Spinner getTrafficChannelPoolSizeSpinner() { if(mTrafficChannelPoolSizeSpinner == null) @@ -553,6 +575,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor @Override protected void setDecoderConfiguration(DecodeConfiguration config) { + getIgnoreCRCChecksumsButton().setDisable(config == null); getIgnoreDataCallsButton().setDisable(config == null); getTrafficChannelPoolSizeSpinner().setDisable(config == null); getTimeslotTable().getItems().clear(); @@ -572,6 +595,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor DecodeConfigDMR decodeConfig = (DecodeConfigDMR)config; getIgnoreDataCallsButton().setSelected(decodeConfig.getIgnoreDataCalls()); + getIgnoreCRCChecksumsButton().setSelected(decodeConfig.getIgnoreCRCChecksums()); getTrafficChannelPoolSizeSpinner().getValueFactory().setValue(decodeConfig.getTrafficChannelPoolSize()); for(TimeslotFrequency timeslotFrequency: decodeConfig.getTimeslotMap()) @@ -581,6 +605,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor } else { + getIgnoreCRCChecksumsButton().setSelected(false); getIgnoreDataCallsButton().setSelected(false); getTrafficChannelPoolSizeSpinner().getValueFactory().setValue(0); getChannelRotationDelaySpinner().getValueFactory().setValue(200); @@ -602,6 +627,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor } // config.setChannelRotationDelay(getChannelRotationDelaySpinner().getValue()); + config.setIgnoreCRCChecksums(getIgnoreCRCChecksumsButton().isSelected()); config.setIgnoreDataCalls(getIgnoreDataCallsButton().isSelected()); config.setTrafficChannelPoolSize(getTrafficChannelPoolSizeSpinner().getValue()); config.setTimeslotMap(new ArrayList<>(getTimeslotTable().getItems())); diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java index 3a9c3b94..6bb71153 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java @@ -94,6 +94,7 @@ public class DMRDecoderState extends TimeslotDecoderState private DMRTrafficChannelManager mTrafficChannelManager; private DecodeEvent mCurrentCallEvent; private long mCurrentFrequency; + private boolean mIgnoreCRCChecksums; private Map mDetectedCallEventsMap = new TreeMap<>(); private static final AddChannelRotationActiveStateRequest CAPACITY_PLUS_ACTIVE_STATE_REQUEST = new AddChannelRotationActiveStateRequest(State.ACTIVE); @@ -116,6 +117,12 @@ public class DMRDecoderState extends TimeslotDecoderState { mNetworkConfigurationMonitor = new DMRNetworkConfigurationMonitor(channel); } + + //For RAS protected systems, allows user to ignore CRC checksums and still decode the system + if(channel.getDecodeConfiguration() instanceof DecodeConfigDMR) + { + mIgnoreCRCChecksums = ((DecodeConfigDMR)channel.getDecodeConfiguration()).getIgnoreCRCChecksums(); + } } /** @@ -190,7 +197,7 @@ public class DMRDecoderState extends TimeslotDecoderState { processData((DataMessage)message); } - else if(message.isValid() && message instanceof LCMessage) + else if((message.isValid() || mIgnoreCRCChecksums) && message instanceof LCMessage) { processLinkControl((LCMessage)message, false); } @@ -204,13 +211,14 @@ public class DMRDecoderState extends TimeslotDecoderState } } //SLCO messages on timeslot 0 to catch capacity plus rest channel events - else if(message.isValid() && message.getTimeslot() == 0 && message instanceof LCMessage) + else if((message.isValid() || mIgnoreCRCChecksums) && message.getTimeslot() == 0 && message instanceof LCMessage) { processLinkControl((LCMessage)message, false); } //Pass the message to the network configuration monitor, if this decoder state has a non-null instance - if(mNetworkConfigurationMonitor != null && message.isValid() && message instanceof DMRMessage) + if(mNetworkConfigurationMonitor != null && (message.isValid() || mIgnoreCRCChecksums) && + message instanceof DMRMessage) { mNetworkConfigurationMonitor.process((DMRMessage)message); } @@ -264,7 +272,7 @@ public class DMRDecoderState extends TimeslotDecoderState */ private void processPacket(DMRPacketMessage packet) { - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA, getTimeslot())); + broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot())); DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp()) .eventDescription(DecodeEventType.DATA_PACKET.name()) @@ -301,7 +309,7 @@ public class DMRDecoderState extends TimeslotDecoderState case MBC_ENC_HEADER: case DATA_ENC_HEADER: case CHANNEL_CONTROL_ENC_HEADER: - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA, getTimeslot())); + broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot())); break; } @@ -325,7 +333,7 @@ public class DMRDecoderState extends TimeslotDecoderState switch(message.getSlotType().getDataType()) { case CSBK: - if(message.isValid() && message instanceof CSBKMessage) + if((message.isValid() || mIgnoreCRCChecksums) && message instanceof CSBKMessage) { processCSBK((CSBKMessage)message); } @@ -360,10 +368,12 @@ public class DMRDecoderState extends TimeslotDecoderState processTerminator((Terminator)message); } break; - case MBC_BLOCK: case RATE_1_OF_2_DATA: case RATE_3_OF_4_DATA: case RATE_1_DATA: + broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot())); + break; + case MBC_BLOCK: case RESERVED_15: case UNKNOWN: default: @@ -410,7 +420,7 @@ public class DMRDecoderState extends TimeslotDecoderState if(csbk instanceof Acknowledge) { DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventDescription(DecodeEventType.RESPONSE.name()) + .eventDescription(DecodeEventType.RESPONSE.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details(((Acknowledge)csbk).getReason().toString()) @@ -425,7 +435,7 @@ public class DMRDecoderState extends TimeslotDecoderState if(csbk instanceof Acknowledge) { DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventDescription(DecodeEventType.RESPONSE.name()) + .eventDescription(DecodeEventType.RESPONSE.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details(((Acknowledge)csbk).getReason().toString()) @@ -442,7 +452,7 @@ public class DMRDecoderState extends TimeslotDecoderState { case AUTHENTICATE_REGISTER_RADIO_CHECK_SERVICE: DecodeEvent registerEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventDescription(DecodeEventType.COMMAND.name()) + .eventDescription(DecodeEventType.COMMAND.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details(DecodeEventType.REGISTER.toString()) @@ -451,7 +461,7 @@ public class DMRDecoderState extends TimeslotDecoderState break; case CANCEL_CALL_SERVICE: DecodeEvent cancelEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventDescription(DecodeEventType.COMMAND.name()) + .eventDescription(DecodeEventType.COMMAND.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details("CANCEL CALL") @@ -462,7 +472,7 @@ public class DMRDecoderState extends TimeslotDecoderState if(csbk instanceof StunReviveKill) { DecodeEvent stunEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventDescription(DecodeEventType.COMMAND.name()) + .eventDescription(DecodeEventType.COMMAND.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details(((StunReviveKill)csbk).getCommand() + " RADIO") @@ -483,7 +493,7 @@ public class DMRDecoderState extends TimeslotDecoderState ServiceRadioCheck src = (ServiceRadioCheck)csbk; DecodeEvent checkEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventDescription(DecodeEventType.RADIO_CHECK.name()) + .eventDescription(DecodeEventType.RADIO_CHECK.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details(src.getServiceDescription() + " SERVICE FOR " + @@ -504,7 +514,7 @@ public class DMRDecoderState extends TimeslotDecoderState if(aloha.hasRadioIdentifier()) { DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventDescription(DecodeEventType.RESPONSE.name()) + .eventDescription(DecodeEventType.RESPONSE.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details("Aloha Acknowledge") @@ -523,7 +533,7 @@ public class DMRDecoderState extends TimeslotDecoderState { case MASS_REGISTRATION: DecodeEvent massEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventDescription(DecodeEventType.REGISTER.name()) + .eventDescription(DecodeEventType.REGISTER.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details("MASS REGISTRATION") @@ -534,7 +544,7 @@ public class DMRDecoderState extends TimeslotDecoderState if(csbk instanceof VoteNowAdvice) { DecodeEvent voteEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventDescription(DecodeEventType.COMMAND.name()) + .eventDescription(DecodeEventType.COMMAND.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details("VOTE NOW FOR " + ((VoteNowAdvice)csbk).getVotedSystemIdentityCode()) @@ -558,7 +568,7 @@ public class DMRDecoderState extends TimeslotDecoderState if(csbk instanceof Protect) { DecodeEvent protectEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventDescription(DecodeEventType.COMMAND.name()) + .eventDescription(DecodeEventType.COMMAND.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details("PROTECT: " + ((Protect)csbk).getProtectKind()) @@ -722,7 +732,7 @@ public class DMRDecoderState extends TimeslotDecoderState if(cmAloha.hasRadioIdentifier()) { DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventDescription(DecodeEventType.RESPONSE.name()) + .eventDescription(DecodeEventType.RESPONSE.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details("Aloha Acknowledge") @@ -1082,7 +1092,7 @@ public class DMRDecoderState extends TimeslotDecoderState ic.update(gpsInformation.getGPSLocation()); DecodeEvent gpsEvent = DMRDecodeEvent.builder(message.getTimestamp()) - .eventDescription(DecodeEventType.GPS.name()) + .eventDescription(DecodeEventType.GPS.toString()) .identifiers(ic) .timeslot(message.getTimeslot()) .details("LOCATION:" + gpsInformation.getGPSLocation()) diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRNetworkConfigurationMonitor.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRNetworkConfigurationMonitor.java index 6280bd62..a278f6ec 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRNetworkConfigurationMonitor.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRNetworkConfigurationMonitor.java @@ -313,7 +313,7 @@ public class DMRNetworkConfigurationMonitor if(mTier3Model != null) { - sb.append("\nNetwork Model").append(mTier3Model); + sb.append("\nNetwork Model: ").append(mTier3Model); } //Observed DMR Channels diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DecodeConfigDMR.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DecodeConfigDMR.java index c82790f1..84b5ccfb 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DecodeConfigDMR.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DecodeConfigDMR.java @@ -39,6 +39,7 @@ public class DecodeConfigDMR extends DecodeConfiguration public static final int CHANNEL_ROTATION_DELAY_MAXIMUM_MS = 2000; private int mTrafficChannelPoolSize = TRAFFIC_CHANNEL_LIMIT_DEFAULT; private boolean mIgnoreDataCalls = true; + private boolean mIgnoreCRCChecksums = false; private List mTimeslotMap = new ArrayList<>(); public DecodeConfigDMR() @@ -90,6 +91,24 @@ public class DecodeConfigDMR extends DecodeConfiguration mIgnoreDataCalls = ignore; } + /** + * Indicates if decoder should ignore CRC checksums when validating decoded messages + */ + @JacksonXmlProperty(isAttribute = true, localName = "ignore_crc") + public boolean getIgnoreCRCChecksums() + { + return mIgnoreCRCChecksums; + } + + /** + * Sets flag to ignore CRC checksums + * @param ignore true to ignore CRC checksums. + */ + public void setIgnoreCRCChecksums(boolean ignore) + { + mIgnoreCRCChecksums = ignore; + } + /** * Traffic channel pool size. * @return diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRTier3Channel.java b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRTier3Channel.java new file mode 100644 index 00000000..824b95dc --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/channel/DMRTier3Channel.java @@ -0,0 +1,40 @@ +package io.github.dsheirer.module.decode.dmr.channel; + +/** + * DMR Tier III Trunking Channel + */ +public class DMRTier3Channel extends DMRLogicalChannel +{ + /** + * Constructs an instance. Note: radio reference uses a one based index, so we add a value of one to the + * calculated logical slot value for visual compatibility for users. + * + * @param channel number or repeater number + * @param timeslot + */ + public DMRTier3Channel(int channel, int timeslot) + { + super(channel, timeslot); + } + + /** + * Logical slot number for this channel. + * + * Formula: LSN = (channel * 2) + timeslot + * + * @return logical slot number, a 1-based index value + */ + @Override + public int getLogicalSlotNumber() + { + int repeater = getRepeater(); + + if(repeater > 0) + { + return ((repeater) * 2) + getTimeslot(); + } + + return 0; + } + +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Clear.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Clear.java index 236c0e68..da594f45 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Clear.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/Clear.java @@ -26,6 +26,7 @@ import io.github.dsheirer.identifier.radio.RadioIdentifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio; @@ -208,7 +209,7 @@ public class Clear extends CSBKMessage implements ITimeslotFrequencyReceiver if(mChannel == null) { - mChannel = new DMRLogicalChannel(getMoveToChannelNumber(), getMoveToTimeslot()); + mChannel = new DMRTier3Channel(getMoveToChannelNumber(), getMoveToTimeslot()); } return mChannel; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/MoveTSCC.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/MoveTSCC.java index ac2d08d4..ca55f6c4 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/MoveTSCC.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/MoveTSCC.java @@ -24,7 +24,7 @@ import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.identifier.radio.RadioIdentifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel; import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio; import io.github.dsheirer.module.decode.dmr.message.CACH; import io.github.dsheirer.module.decode.dmr.message.data.SlotType; @@ -156,7 +156,7 @@ public class MoveTSCC extends CSBKMessage } else { - mDMRChannel = new DMRLogicalChannel(getMessage().getInt(CHANNEL_NUMBER), 1); + mDMRChannel = new DMRTier3Channel(getMessage().getInt(CHANNEL_NUMBER), 1); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AdjacentSiteInformation.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AdjacentSiteInformation.java index beedd447..3905a3f2 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AdjacentSiteInformation.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AdjacentSiteInformation.java @@ -24,6 +24,7 @@ import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.message.CACH; @@ -121,7 +122,7 @@ public class AdjacentSiteInformation extends Announcement implements ITimeslotFr if(mChannel == null) { //Timeslot hard-coded to 1 - mChannel = new DMRLogicalChannel(getMessage().getInt(NEIGHBOR_CHANNEL_NUMBER), 1); + mChannel = new DMRTier3Channel(getMessage().getInt(NEIGHBOR_CHANNEL_NUMBER), 1); } return mChannel; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AnnounceWithdrawTSCC.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AnnounceWithdrawTSCC.java index 7c056fd1..4dd2ca29 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AnnounceWithdrawTSCC.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/AnnounceWithdrawTSCC.java @@ -23,7 +23,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; -import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel; import io.github.dsheirer.module.decode.dmr.message.CACH; import io.github.dsheirer.module.decode.dmr.message.data.SlotType; import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock; @@ -221,7 +221,7 @@ public class AnnounceWithdrawTSCC extends Announcement } else { - mChannel1 = new DMRLogicalChannel(getMessage().getInt(CHANNEL_NUMBER_1), 1); + mChannel1 = new DMRTier3Channel(getMessage().getInt(CHANNEL_NUMBER_1), 1); } } @@ -235,7 +235,7 @@ public class AnnounceWithdrawTSCC extends Announcement { if(mChannel2 == null) { - mChannel2 = new DMRLogicalChannel(getMessage().getInt(CHANNEL_NUMBER_2), 1); + mChannel2 = new DMRTier3Channel(getMessage().getInt(CHANNEL_NUMBER_2), 1); } return mChannel2; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/VoteNowAdvice.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/VoteNowAdvice.java index 29fd76c7..96ad4d92 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/VoteNowAdvice.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/announcement/VoteNowAdvice.java @@ -24,6 +24,7 @@ import io.github.dsheirer.identifier.Identifier; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.message.CACH; @@ -203,7 +204,7 @@ public class VoteNowAdvice extends Announcement implements ITimeslotFrequencyRec if(mChannel == null) { - mChannel = new DMRLogicalChannel(getMessage().getInt(VOTED_CHANNEL_NUMBER), 1); + mChannel = new DMRTier3Channel(getMessage().getInt(VOTED_CHANNEL_NUMBER), 1); } return mChannel; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/grant/ChannelGrant.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/grant/ChannelGrant.java index e01d303f..47e54a3a 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/grant/ChannelGrant.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/standard/grant/ChannelGrant.java @@ -23,6 +23,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.module.decode.dmr.DMRSyncPattern; import io.github.dsheirer.module.decode.dmr.channel.DMRChannel; import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel; +import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel; import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.message.CACH; @@ -148,7 +149,7 @@ public abstract class ChannelGrant extends CSBKMessage implements ITimeslotFrequ if(mChannel == null) { - mChannel = new DMRLogicalChannel(getChannelNumber(), getChannelGrantTimeslot()); + mChannel = new DMRTier3Channel(getChannelNumber(), getChannelGrantTimeslot()); } return mChannel; diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCMessageFactory.java index 2f9f4acf..3dfc78ca 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCMessageFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCMessageFactory.java @@ -41,6 +41,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ControlChanne import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.HyteraXPTChannel; import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.NullMessage; import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ShortLCMessage; +import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.TrafficChannelSystemParameters; import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.UnknownShortLCMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -152,11 +153,13 @@ public class LCMessageFactory case SHORT_STANDARD_CONTROL_CHANNEL_SYSTEM_PARAMETERS: slc = new ControlChannelSystemParameters(message, timestamp, timeslot); break; + case SHORT_STANDARD_TRAFFIC_CHANNEL_SYSTEM_PARAMETERS: + slc = new TrafficChannelSystemParameters(message, timestamp, timeslot); + break; case SHORT_HYTERA_XPT_CHANNEL: case SHORT_STANDARD_XPT_CHANNEL: slc = new HyteraXPTChannel(message, timestamp, timeslot); break; - case SHORT_STANDARD_TRAFFIC_CHANNEL_SYSTEM_PARAMETERS: case SHORT_STANDARD_UNKNOWN: default: slc = new UnknownShortLCMessage(message, timestamp, timeslot); diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/TrafficChannelSystemParameters.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/TrafficChannelSystemParameters.java new file mode 100644 index 00000000..8dd70988 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/shorty/TrafficChannelSystemParameters.java @@ -0,0 +1,112 @@ +/* + * ***************************************************************************** + * 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 + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.dmr.message.data.lc.shorty; + +import io.github.dsheirer.bits.CorrectedBinaryMessage; +import io.github.dsheirer.identifier.Identifier; +import io.github.dsheirer.module.decode.dmr.message.type.SystemIdentityCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * DMR Tier III - Traffic Channel System Parameters + */ +public class TrafficChannelSystemParameters extends ShortLCMessage +{ + private static final int SYSTEM_IDENTITY_CODE_OFFSET = 4; + private static final int REGISTRATION_REQUIRED_FLAG = 18; + private static final int[] COMMON_SLOT_COUNTER = new int[]{19, 20, 21, 22, 23, 24, 25, 26, 27}; + + private SystemIdentityCode mSystemIdentityCode; + private List mIdentifiers; + + /** + * Constructs an instance + * + * @param message containing link control data + */ + public TrafficChannelSystemParameters(CorrectedBinaryMessage message, long timestamp, int timeslot) + { + super(message, timestamp, timeslot); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + if(!isValid()) + { + sb.append("[CRC ERROR] "); + } + sb.append("SLC TIER III TRAFFIC CHANNEL ").append(getSystemIdentityCode().getModel()); + sb.append(" NET:").append(getSystemIdentityCode().getNetwork()); + sb.append(" SITE:").append(getSystemIdentityCode().getSite()); + if(isRegistrationRequired()) + { + sb.append(" REGISTRATION REQUIRED"); + } + sb.append(" SLOT COUNTER:").append(getCommonSlotCounter()); + sb.append(" MSG:").append(getMessage().toHexString()); + return sb.toString(); + } + + /** + * System Identity Code structure + */ + public SystemIdentityCode getSystemIdentityCode() + { + if(mSystemIdentityCode == null) + { + mSystemIdentityCode = new SystemIdentityCode(getMessage(), SYSTEM_IDENTITY_CODE_OFFSET, false); + } + + return mSystemIdentityCode; + } + + /** + * Indicates if registration is required to access this network + */ + public boolean isRegistrationRequired() + { + return getMessage().get(REGISTRATION_REQUIRED_FLAG); + } + + /** + * Common Slot Counter + */ + public int getCommonSlotCounter() + { + return getMessage().getInt(COMMON_SLOT_COUNTER); + } + + @Override + public List getIdentifiers() + { + if(mIdentifiers == null) + { + mIdentifiers = new ArrayList<>(); + mIdentifiers.add(getSystemIdentityCode().getNetwork()); + mIdentifiers.add(getSystemIdentityCode().getSite()); + } + + return mIdentifiers; + } +}